如何使用数据和可编码的Swift优化NSAttributedString的存储?

时间:2018-11-24 19:58:33

标签: swift codable

保存NSTextView的内容(即其NSTextStorage属性,本身就是NSAttributedString时,我正在尝试优化存储空间。

将其另存为Data(例如,使用rtfd(from:documentAttributes:)方法,并作为Codable结构的一部分)会导致字符串很大,比内容本身大得多,尤其是当将图像插入NSTextView中。例如,插入200K图像将生成5MB的JSON文件。

旁注:Data对象被直接编码而不是作为编码对象的属性,甚至更糟,因为它以小整数数组而不是任意字符串的形式编码。我不确定为什么,尽管可以通过将Data插入简单的包装器结构中来防止这种情况。

奇怪的是,使用ZIP压缩实际的JSON文件仍会产生4MB的文件,仅增加20%,因此我不清楚200K图像如何变成如此庞大,难以压缩的编码字符串。

我想弄清楚使用NSAttributedString协议有效存储Codable的正确方法是什么。任何提示或建议都非常感谢。

我还想知道Codable是否有有效的二进制编码选项。

1 个答案:

答案 0 :(得分:7)

TL; DR:RTFD将图像编码为PNG,但是您可以使其编码为JPG,以节省空间。如果您有时间创建自定义格式,则可能会更好,更轻松。

NSAttributedString可以编码为HTML,rtf,rtfd,纯文本,多种Office / Word格式等。鉴于每种格式都是一种官方格式,必须遵循官方规范,因此没有除了节省空间外,还有很多事情可以做:

  1. 选择最适合您的用例且占用空间最小的受支持格式。

OR

  1. 编写自己的格式。

方法1:RTFD

在受支持的格式中,RTFD确实确实适合您的用例,因为它包括对图像等附件的支持。随时尝试其他包含的格式,其描述在下面的“其他格式”中。

  

将其另存为数据(例如,使用rtfd(from:documentAttributes :)方法),并作为Codable结构的一部分,导致字符串很大,比内容本身大得多,尤其是在将图像插入NSTextView时。例如,插入200K图像将生成5MB的JSON文件。

要了解此处发生的情况,请尝试以下代码:

do {
    let rtfd = try someAttributedString.rtfdFileWrapper(from: NSRange(location: 0, length: someAttributedString.length), documentAttributes: [:])
    rtfd?.write(to: URL(fileURLWithPath: "/Users/yourname/someFolder/RTFD.rtfd"), options: .atomic, originalContentsURL: nil)
} catch {
    print("\(error)")
}

致电rtfd(from:documentAttributes:)时,您的电话Data越来越平坦。然后可以将此平面数据编码到某个位置,然后读回到NSAttributedString中。但是请不要误会:RTFD是一种软件包格式(“ D”代表目录)。因此,通过调用rtfdFileWrapper(from:documentAttributes:)并将其写入扩展名为URL的{​​{1}},我们可以看到rtfd复制的实际包格式,但它是目录而不是目录原始数据。在Finder中,右键单击生成的文件,然后选择“显示包内容”。

RTFD软件包包含一个RTF文件(用于指定文本和属性)以及每个附件的副本。那么,为什么您的榜样那么大?在我的测试中,答案似乎是RTFD希望以PNG格式找到其图像。调用rtfd(from:documentAttributes:)rtfdFileWrapper(from:documentAttributes:)时,任何图像附件似乎都被写为PNG文件,这会占用更多空间。发生这种情况是因为您的图像先被包裹在rtfd(from:documentAttributes:)中,然后又被包裹在NSImage中。 NSTextAttachment能够以其他格式写出图像数据,包括PNG等较大的格式。

我假设您尝试的图像采用的是JPEG之类的压缩格式,并且NSImage以PNG格式写入RTFD。

改为使用NSAttributedString

假设您对图像进行了压缩并且没有Alpha通道之类的信息,则应该能够创建包含JPEG张图像的RTFD文件。

例如,我只用原始的JPG替换了生成的PNG图像,就可以将RTFD文件从12 MB以上(大图像)减小到2.8 MB。最初,这对于TextEdit是不可接受的,但是后来我将图像的文件扩展名更改为jpg(即使它仍然是JPG),并且它接受了。

在代码中,它甚至更简单。您可能只需改变添加图像附件的方式就可以摆脱困境。

.png

然后,当您使用该// Don't do this unless you want PNG let image = NSImage(contentsOf: ...) // NSImage will write to a larger PNG file let attachment = NSTextAttachment() attachment.image = image // Do this if you want smaller files let image = try? Data(contentsOf: ...) // This will remain in raw JPG format let attachment = NSTextAttachment(data: image, ofType: kUTTypeJPEG as String) // Explicitly specify JPG 创建一个新的NSAttributedString并将其附加到NSTextAttachment时,写入RTFD数据将大大减少。

当然,如果您依靠Cocoa UI / API附加图像,则可能无法控制此过程。这可能会使过程更加困难,您可能需要通过交换图像来修改生成的数据。

方法2:自定义格式

由于无法控制附件添加过程并且需要平面数据,因此上述方法可能不方便。在这种情况下,自定义格式可能会更好。

没有什么可以阻止您设计自己的格式(二进制,文本,包等),然后为其编写编码器。您可以指定特定的图像格式或支持多种格式。由你决定。而且,除非您是高级文字处理程序,否则您可能不需要一直存储所有属性,例如font。

  

我还想知道Codable是否有有效的二进制编码选项。

首先,请注意NSTextStorage是一个Objective-C类(在Apple平台上使用时),并且符合NSAttributedString而不是NSSecureCoding

请注意,您无法扩展Codable使其符合NSAttributedString,因为Codable的{​​{1}}要求只能通过确保初始化程序将包含在所有目录中来满足。子类。由于此类不是init(from:),这意味着只能由Decodable来满足。必需的初始化程序只能在原始声明中指定,而不能在扩展名上指定。

因此,如果要使其符合final,则需要使用包装器对象。 required init对于获取需要编码的属性和原始字符应该会有所帮助,但是您还必须确保注意图像。

对于二进制编码,Codable完全与格式无关,因此您可以编写符合enumerateAttributes(in:options:using:)的对象,该对象可以执行您想要的任何事情,包括使用原始字节存储所有内容。

除了:其他格式

以下是其他受支持格式的简要介绍(按大小顺序)。在这些测试中,我在系统字体中使用了非常小的字符串Codable。每个格式说明后(在括号中)是存储该字符串的字节数。

  • 纯文本可以将上述格式存储为36个字节(每个字符1个),但不会保留属性或附件。 (36个字节)
  • 如果您需要保留属性而非附件,则
  • RTF 似乎是最轻巧的。 (331个字节)
  • HTML 是第二轻,但并非真正设计成存储格式。以我的经验,当Coder转换为HTML时,行间距等属性会丢失。 (536个字节)
  • 如果您只需要与Apple平台兼容并且不喜欢上述格式,那么使用"Hello World! There's so much to see!"时创建的
  • 二进制Plist 是一个不错的选择。此格式也支持图像,但通常仍大于上述格式(和RTFD)。 (648字节)
  • 紧随其后的是
  • Web存档,但我不建议使用它,因为WebKit已弃用它。 Safari在某些方面仍然使用它。 (784字节)
  • Word ML 可能仅对已经知道他们需要它的人有用。这种格式及其下的所有内容通常都会有一堆样板,随着文本的添加,样板将占文件的比例较小。 (〜1.2 MB)
  • 打开文档(OASIS)比大多数Word格式要小,但是如果没有充分的理由,您可能不会使用它。 (〜2.4 MB)
  • Office Open XML 是您仅在完全需要该格式时使用的另一种格式。 (〜3.5 MB)
  • 文档(Microsoft Word)与少量文本相比,该文件非常大。虽然我希望这种格式允许图像,但在我的测试中,添加一个文件时,文件大小实际上并未增加。 (〜19.4 MB)
  • Mac Simple Text 似乎总是产生错误。 (不适用)

最后通知

最后,随着Foundation继续适应Swift而不是Objective-C,NSAttributedString的编码体验应该会更好。您可以想象有一天NSKeyedArchiver或类似的Swifty类型符合NSAttributedString,然后可以与任何文件格式NSAttributedString配对的情况。