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

标签 swift codable

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

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

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

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

我想弄清楚使用NSAttributedString协议(protocol)有效存储Codable的正确方法是什么。任何提示或建议,不胜感激。

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

最佳答案

TL; DR:RTFD将图像编码为PNG,但您可以改为编码JPG以节省空间。如果您有时间创建自定义格式,则可能会更好,更轻松。
NSAttributedString可以编码为HTML,rtf,rtfd,纯文本,多种Office/Word格式等。鉴于每种格式都是一种官方格式,必须遵循官方规范,因此在术语方面做不到很多除以下以外节省空间:

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

  • 要么
  • 编写自己的格式。

  • 方法1:RTFD

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

    Saving it as Data, for example using the rtfd(from:documentAttributes:) method, and as part of a Codable structure, results in a very large string, much larger than the content itself especially when inserting an image into the NSTextView. For example, inserting a 200K image will result in a 5MB JSON file.



    要了解这里发生了什么,请尝试以下代码:

    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扩展名的rtfd中,我们可以看到rtfd(from:documentAttributes:)复制的实际包格式,但它是目录而不是原始数据。在Finder中,右键单击生成的文件,然后选择“显示包内容”。

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

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

    使用JPEG代替

    假设您对图像进行了压缩并且没有Alpha通道之类的信息,那么您应该能够使用jpg图像创建RTFD文件。

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

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

    // 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并将其附加到NSTextStorage时,写入RTFD数据将大大减少。

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

    方法2:自定义格式

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

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

    I am also wondering whether there is a valid binary encoding option for Codable.



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

    请注意,您不能扩展NSAttributedString以使其符合Codable,因为只有通过确保初始化程序也将包括在所有子类中,才能满足init(from:)Decodable要求。由于此类不是final,所以这只能由required init满足。只能在原始声明上指定必需的初始化程序,而不能在扩展名上指定。

    因此,如果要使其符合Codable,则需要使用包装器对象。 enumerateAttributes(in:options:using:)应该有助于获取需要编码的属性和原始字符,但是您还需要确保也要注意图像。

    至于二进制编码,Codable完全与格式无关,因此您可以编写符合Coder的自己的对象,该对象可以执行所需的任何操作,包括使用原始字节存储所有内容。

    旁:其他格式

    以下是其他受支持格式的简要说明(按大小顺序)。在这些测试中,我在系统字体中使用了非常小的字符串"Hello World! There's so much to see!"。在每种格式说明之后(在括号中)是存储该字符串的字节数。
  • 纯文本可以将上述格式存储为36个字节(每个字符1个),但不会保留属性或附件。 (36个字节)
  • RTF 似乎是最轻量的,如果您需要保留属性而不是附件。 (331字节)
  • HTML 是第二轻,但并非真正设计为存储格式。以我的经验,当通过NSAttributedString转换为HTML时,行间距等某些属性会丢失。 (536字节)
  • 如果您只需要与Apple平台兼容并且不喜欢上述格式,则使用NSKeyedArchiver时创建的
  • 二进制列表是一个不错的选择。此格式也支持图像,但通常仍大于上述格式(和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简单文本似乎总是产生错误。 (不适用)

  • 最后说明

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

    关于swift - 如何使用数据和可编码的Swift优化NSAttributedString的存储?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53461875/

    相关文章:

    ios - 完全禁用 Swift 按下 'backspace'

    ios - 使用 Codable 或 ObjectMapper 映射通用 API 响应

    Swift Codable Type 编码成功但解码失败

    ios - dyld : Library not loaded Reason: no suitable image found. 是否找到:/private/var/mobile/Containers/Bundle/Application…

    ios - UILabel TapGesture 未触发

    swift - 将 View 提取为函数或结构?

    json - 我如何从 JSON 建模一个结构,其中一些高级属性没有键

    swift - Codable/Decodable 应该用字符串解码数组

    ios - 使用 Codable 协议(protocol)按照从服务器接收到的相同顺序解码 json

    ios - 域 NSURLErrorDomain 代码 4294966292