PDF分割器
- Unity-PDF分割器
- 前言
- 核心思路
- 解决过程
- 一、Unity安装iTextSharp
- 二、运行时计算将要生成文件的大小
- 三、分割核心代码
- 四、使用StandaloneFileBrowser
- 五、其他的一些脚本
- 六、游戏界面主体的构建
- MainWindow
- WarningPanel & FinishPanel
- By-Round Moon
Unity-PDF分割器
PDFSplitter 资源包下载链接【CSDN】免费
前言
最近有这么一个需求,PDF太大了,需要拆分成多份,要求每份的PDF不大于10MB。刚开始,我认为这应该很简单,但是足足耗费了一天的时间去解决该问题。
核心思路
首先是核心部分当然是PDF如何进行分割了,不仅仅要分割,还要统计分割的大小。然后我还不想伤硬盘,直接写在硬盘上判断文件是否超过了大小,然后回溯。所以我只能在运行的时候检测文件大小。核心部分的难度基本如上所述。下面来讲解一下我是如何解决这个问题的吧。还有一个最难的问题,那就是如何不用UnityEditor库来打开FilePanel
又叫FileDialog
。
解决过程
一、Unity安装iTextSharp
首先在Visual Studio Community中安装.NET 桌面开发,用来下载需要的dll。当然也可以直接在Unity中打开VS后下载,我这其实是脱裤子放屁,但是我想保持Unity的干净,不想事后卸载删除,所以我采用了这个方法。
安装完成后创建一个控制台应用。
将框架选为.NET Framework 2.0。
打开后,在 工具》NuGet包管理器》管理解决方案的NuGet程序包。在浏览中搜索iTextSharp
并安装。安装完成后,在.sln
文件的同路径下有一个packages包,把里面BouncyCastle.1.8.9
和iTextSharp.5.5.13.3
的lib文件夹下的.dll
文件拷贝到Unity项目的Plugins(自己创建)文件夹中。
二、运行时计算将要生成文件的大小
明确已知,没有提供方法来确定生成后的大小。但是我们可以先在MemoryStream
中存储一下,并获取MemoryStream
空间所消耗的大小。
下面给出示例代码。
long GetMemorySize(string inputFilePath){PdfReader reader = new PdfReader(inputFilePath);Document document = new Document();MemoryStream memoryStream = new MemoryStream();PdfCopy copy = new PdfCopy(document, memoryStream);document.Open();copy.AddPage(copy.GetImportedPage(reader, 1));//这里页码是从1到最后的,不是常规的从0开始。long memorySize = memoryStream.Length;reader.Close();document.Close();memoryStream.Close();return memorySize;}
三、分割核心代码
这份脚本为PDFSplitter
。将来挂在GameController
空物体上。
using UnityEngine;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
using UnityEngine.UI;
using System.Collections.Concurrent;
using System.Collections;public class PDFSplitter : MonoBehaviour
{private string inputFilePath;private string outputDirectory;private long maxFileSizeMB = 10 * 1024 * 1024;private PdfReader reader;public InputField from;public InputField save;public Slider slider;private ConcurrentQueue<float> resultQueue = new ConcurrentQueue<float>();public void Splitter(){System.Threading.Thread thread = new System.Threading.Thread(Run);thread.Start();StartCoroutine(updateSlider());}IEnumerator updateSlider(){float res;while (true){while (!resultQueue.TryDequeue(out res)) ;if (res < 0){slider.value = 1.0f;GetComponent<GameController>().showFinishPanel = true;break;}slider.value = res;yield return null;}}public void Run(){int cnt = 1;int pre = 1;float rate;inputFilePath = from.text;outputDirectory = save.text;reader = new PdfReader(inputFilePath);PdfReader.unethicalreading = true;Document document = null;MemoryStream memoryStream = null;PdfCopy copy = null;int pageCount = reader.NumberOfPages;for (int i = 1; i <= pageCount; i++){if (document == null){document = new Document();memoryStream = new MemoryStream();copy = new PdfCopy(document, memoryStream);document.Open();copy.AddPage(copy.GetImportedPage(reader, i));}else{copy.AddPage(copy.GetImportedPage(reader, i));if (memoryStream.Length > maxFileSizeMB){PDF_Writer(pre, i - 1, cnt++.ToString("00"));document.Close();memoryStream.Close();document = null;memoryStream = null;copy = null;pre = i--;}}rate = 1.0f * i / pageCount;resultQueue.Enqueue(rate);}document.Close();PDF_Writer(pre, pageCount, cnt.ToString("00"));reader.Close();resultQueue.Enqueue(-1.0f);}void PDF_Writer(int start, int end, string name){Document document = new Document();PdfCopy copy = new PdfCopy(document, new FileStream(outputDirectory + $"//{name}.pdf", FileMode.Create));document.Open();for (int i = start; i <= end; i++)copy.AddPage(copy.GetImportedPage(reader, i));document.Close();}
}
四、使用StandaloneFileBrowser
首先在Github链接中下载StandaloneFileBrowser。
之后,就将下载的Packages直接拖进项目中导入。
用这个包之后为了能够打包成功我们需要将Unity的.Net框架改为 .Net 4.x
我们这里只需要使用StandaloneFileBrowser.OpenFilePanel
和StandaloneFileBrowser.OpenFolderPanel
可能导入时候会出一些莫名其妙的错误,不用在意,只要能编译正常用,打包正常打出去,就可以。奇怪的是打包成功之后这些错误莫名其妙的消失掉了。
这份脚本为PathBar
。将来挂在涉及路径选择的自定义物体上。
using UnityEngine;
using UnityEngine.UI;
using SFB;public class PathBar : MonoBehaviour
{[HideInInspector]public string path;public InputField info;public void SelectFilePath(){var extensions = new[] { new ExtensionFilter("PDF Files", "pdf"), new ExtensionFilter("All Files", "*") };path = StandaloneFileBrowser.OpenFilePanel("Open PDF File", "", extensions, false)[0];info.text = path;}public void SelectFolderPath(){path = StandaloneFileBrowser.OpenFolderPanel("Select save Folder", "", false)[0];info.text = path;}}
五、其他的一些脚本
这份脚本为GameController
。将来挂在GameController
空物体上。
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using System.IO;
public class GameController : MonoBehaviour
{public GameObject FinishPanel;public GameObject WarningPanel;[HideInInspector]public bool showFinishPanel;public InputField from;public InputField save;public void Run(){if (!File.Exists(from.text) || !Directory.Exists(save.text) || Path.GetExtension(from.text).ToLower() != ".pdf"){WarningPanel.GetComponent<PanelController>().Open();return;}showFinishPanel = false;GetComponent<PDFSplitter>().Splitter();StartCoroutine(ShowFinishPanel());}IEnumerator ShowFinishPanel(){while (!showFinishPanel)yield return null;FinishPanel.GetComponent<PanelController>().Open();}
}
这份脚本为PanelController
。将来挂在一些用来提示的物体上。
using UnityEngine;public class PanelController : MonoBehaviour
{public void Close(){gameObject.SetActive(false);}public void Open(){gameObject.SetActive(true);}
}
六、游戏界面主体的构建
MainWindow
界面如下,很好拆分上面的我称之为PathBar,用来选择路径。
中间是Button,用来执行分割操作。
最底下是进度条,可以参考B站视频 BV1iW411D78W
大概在55分钟左右会有讲解。我这里多做了一步操作,把slider的图片全部设置成了None。
下面看一下我的目录结构
WarningPanel & FinishPanel
下面看一下我的目录结构
PDFSplitter 资源包下载链接【CSDN】免费