作者: kanweiwei

  • 如何使用Office图片压缩功能

    本教程适用:怡氧桌面端

    功能简介

    在怡氧桌面端 v2.5.0 中,针对用户文档中图片体积过大导致的协作加载缓慢问题,新增“图片压缩”功能。该功能可智能压缩文档内图片体积(支持JPEG/PNG格式),可减少50%-80%的图片占用空间,同时保持人眼可接受的清晰度。

    方法/步骤

    第一步:打开文档

    1. 启动怡氧桌面端,打开需要压缩图片的文档
    2. 确保文档中包含需要压缩的图片(目前仅支持JPG/PNG格式)

    第二步:启用图片压缩

    1. 选中图片,可按住 ctrl 键多选图片
    2. 点击右侧工具栏的【图像设置】,在功能面板中选择【压缩图片】
    3. 或右键点击图片,选择菜单中的【压缩图片】

    第三步:设置压缩参数

    在弹出窗口中,顶部可选择:

    • 压缩文档中所有图片
    • 压缩所选图片

    如果文档需要压缩的图片很多,请直接选择【压缩文档中所有图片】

    压缩选项中可以选择:9最佳、8高质量、5质量、3低质量

    第四步:执行压缩

    确认后点击 「确定」,等待进度条完成(大文档约需10-30秒)

    常见问题

    Q:压缩后图片会模糊吗?

    A:不会模糊,图片仍保持原有的分辨率

    Q:能否单独恢复某张图片?

    A:无法恢复,操作前请务必备份文档

    Q:支持压缩PDF中的图片吗?

    A:当前仅支持Office三件套文档类型

    提示:示例文档原尺寸 9.32MB,选择默认的“9最佳”,压缩后的文档大小为2.11MB,减少了77.3%的空间

  • 怡氧桌面端支持 WPS 单元格图片(DISPIMG)公式解析

    背景介绍

    想象一下,你在制作一份电子表格,需要在某些单元格中放入产品图片。在 Excel 中,你只能把图片放在表格的”上层”,就像在表格上贴了一张贴纸。这样的图片是独立的,当你移动或复制单元格时,图片并不会跟着走。

    WPS 为了解决这个问题,创造性地提出了 DISPIMG 公式。这个方案就像是把图片”嵌入”到了单元格中,让图片真正成为单元格的一部分。这样,无论你怎么移动或复制单元格,图片都会跟着走。

    实现机制

    WPS 的单元格图片功能通过三个关键文件相互配合来实现:

    1. 工作表引用(sheet1.xml)- 图片的”索引卡”

    想象你在图书馆查找一本书,首先会查看索引卡片。这里的 DISPIMG 公式就像是一张索引卡,记录了”这个单元格要显示哪张图片”的信息。

    在工作表中,通过 DISPIMG 函数来引用单元格图片:

    <sheetData>
        <row r="1" ht="14.25">
            <c r="C1" t="str">
                <f>DISPIMG("ID_A16C8CE6E39B4F25934D584A1C41E1E1",1)</f>
                <v>=DISPIMG("ID_A16C8CE6E39B4F25934D584A1C41E1E1",1)</v>
            </c>
        </row>
    </sheetData>

    其中, DISPIMG 函数接受两个参数:

    • 图片的唯一标识符(ID)
    • 图片的展示方式(模式参数)

    2. 图片定义(cellimages.xml)- 图片的”档案卡”

    每张图片都有一个详细的”档案卡”(存储在 cellimages.xml 中),记录了这张图片的所有重要信息:

    • 图片的唯一编号(就像身份证号)
    • 图片的显示方式(如何摆放、多大尺寸等)
    • 图片实际存储位置的线索

    特别值得注意的是,WPS 在设计这个”档案卡”时,采用了与 Office 文档相同的标准格式(OOXML)。这不仅确保了其他软件能理解这些信息,更重要的是,这样的设计让开发者可以复用处理传统 Office 浮动图片的代码逻辑,大大减少了重复开发的工作量。

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <etc:cellImages
        xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
        xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
        xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
        xmlns:etc="http://www.wps.cn/officeDocument/2017/etCustomData">
    
        <etc:cellImage>
            <xdr:pic>
                <!-- 1. 图片标识信息 -->
                <xdr:nvPicPr>
                    <xdr:cNvPr id="1773908100" name="ID_A16C8CE6E39B4F25934D584A1C41E1E1"/>
                    <xdr:cNvPicPr>
                        <a:picLocks noChangeAspect="1"/>
                    </xdr:cNvPicPr>
                </xdr:nvPicPr>
    
                <!-- 2. 图片资源引用 -->
                <xdr:blipFill>
                    <a:blip r:embed="rId1"/>
                    <a:stretch/>
                </xdr:blipFill>
    
                <!-- 3. 图片显示属性 -->
                <xdr:spPr bwMode="auto">
                    <a:xfrm>
                        <a:off x="0" y="0"/>
                        <a:ext cx="59436000" cy="39623999"/>
                    </a:xfrm>
                    <a:prstGeom prst="rect">
                        <a:avLst/>
                    </a:prstGeom>
                </xdr:spPr>
            </xdr:pic>
        </etc:cellImage>
    </etc:cellImages>

    3. 资源映射(_rels/cellimages.xml.rels)

    最后,还需要一个清单(cellimages.xml.rels)来记录每张图片实际存放的位置,就像图书馆中的架位表,告诉你具体去哪个书架找这本书。

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
        <Relationship
            Id="rId1"
            Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
            Target="media/image1.png"/>
        <Relationship
            Id="rId2"
            Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
            Target="media/image2.png"/>
    </Relationships>

    引用链路说明

    1. 工作表到图片定义的关联

    • 工作表中的 DISPIMG 函数通过图片 ID 引用图片
    • 图片 ID 对应 cellimages.xml 中 xdr:cNvPr 的 name 属性
    • 这确保了每个单元格能准确找到其对应的图片定义

    2. 图片定义到实际资源的关联

    • cellimages.xml 中通过 r:embed 属性引用图片资源
    • r:embed 的值(如 “rId1″)对应 cellimages.xml.rels 中的 Id
    • cellimages.xml.rels 将这个 Id 映射到实际的图片文件路径

    3. 资源存储结构

    xl/
    ├── _rels/
    │   └── cellimages.xml.rels
    ├── media/
    │   ├── image1.png
    │   └── image2.png
    └── worksheets/
        └── sheet1.xml
    └── cellimages.xml

    为什么要这样设计?

    这种设计的巧妙之处在于:

    1. 模块化:每个部分职责明确,就像图书馆中的索引系统、档案系统和实际书架是分开管理的。这样便于维护和更新。
    2. 标准化:采用通用的 OOXML 标准,就像使用通用的图书分类法,确保其他软件也能读懂这些信息。
    3. 灵活性:图片可以方便地随单元格移动,就像书籍可以轻松地在书架间调整位置,而索引系统会自动更新。
    4. 高效性:通过 ID 和引用关系,系统可以快速定位到需要的图片,就像图书馆的编码系统让查找书籍变得简单高效。

    这种设计让整个功能既实用又可靠,就像一个运作良好的图书管理系统,每个部分都各司其职,又紧密配合。

    前端渲染实现

    在前端实现中,需要特别处理 DISPIMG 公式的渲染逻辑:

    1. 公式识别
    • 在非编辑模式下,首先识别单元格内容是否为 DISPIMG 公式
    • 解析公式参数,获取图片 ID 和显示模式
    1. 图片渲染
    • 根据图片 ID 获取对应的图片资源
    • 使用 Canvas API 进行图片绘制
    • 通过 canvas.clip() API 限制绘制区域在当前单元格范围内,避免图片溢出到其他单元格
    • 使用 drawImage() API 将图片绘制到画布上

    示例代码:

    function renderCellImage(ctx, cell, image) {
      // 保存当前画布状态
      ctx.save();
    
      // 创建裁剪路径(限制在单元格范围内)
      ctx.beginPath();
      ctx.rect(cell.x, cell.y, cell.width, cell.height);
      ctx.clip();
    
      // 绘制图片
      ctx.drawImage(image, cell.x, cell.y, cell.width, cell.height);
    
      // 恢复画布状态
      ctx.restore();
    }

    结论

    WPS 的单元格图片功能通过巧妙的文件组织和引用机制,实现了图片与单元格的绑定。这种实现方式不仅保持了与标准 Office Open XML 的兼容性,还提供了灵活的图片管理和显示控制能力。理解这种实现机制对于开发类似功能或进行文件格式兼容都有重要的参考价值。

  • 怡氧桌面端 Office 文档中的图片压缩实现

    引言

    在当今数字化办公环境中,Office 文档已成为企业日常协作的重要载体。随着高清图片的广泛应用,文档中的图片不仅丰富了表达内容,提升了文档的可读性和专业性,但同时也带来了一系列技术挑战:

    • 存储负担:大量高清图片会显著增加文档体积,占用企业宝贵的存储资源
    • 单张高清图片可能达到数 MB 大小
    • 一份文档动辄包含数十张图片
    • 企业级存储成本居高不下
    • 传输效率:过大的文件会影响文档的上传下载速度
    • 网络带宽受限时下载缓慢
    • 移动办公场景下体验更差
    • 多人同时访问时服务器压力大
    • 协作体验:文档加载缓慢会影响多人协作效率
    • 打开大文档耗时长
    • 页面渲染卡顿
    • 实时协作延迟高
    • 影响团队工作效率
    • 版本管理:大体积文档的版本控制也面临挑战
    • 占用更多版本存储空间
    • 历史版本对比耗时增加
    • 文档备份成本提高

    为了优化这些问题,我们在怡氧桌面端中开发了智能图片压缩功能。该功能可以在保证图片视觉质量的前提下,有效降低文档体积,提升协作效率。本文将从技术选型、压缩策略、性能优化等多个维度,详细介绍这一功能的设计思路和实现细节。

    技术方案概述

    我们的技术选型主要基于以下考虑:

    1. Sharp:选择 Sharp 作为核心图像处理库
    • 基于 libvips 实现,C++ 底层保证高性能
    • 内存占用低,适合批量处理
    • 支持主流图片格式,压缩效果优异
    1. Electron Utility Process
    • 将 CPU 密集型的压缩任务隔离到独立进程
    • 避免阻塞主进程,保证界面响应流畅
    1. p-limit
    • 智能控制并发任务数量
    • 优化系统资源利用

    压缩策略详解

    在怡氧桌面端中,我们主要处理 JPEG 和 PNG 这两种最常见的图片格式。针对不同的格式,我们采用了不同的压缩策略。

    JPEG 压缩策略

    JPEG 格式主要用于照片等色彩丰富的图像,我们采用以下策略:

    1. 质量控制
    • 默认质量值设置为 75
    • 通过 estimateQuality 函数评估原图质量
    • 压缩后的质量不超过原图质量
    1. 优化选项
    • 启用 mozjpeg 优化
    • 保留重要的元数据信息
    • 移除不必要的颜色配置文件

    示例代码:

    async function compressJPEG(buffer: Buffer, qualityFactor: number) {
      const originalQuality = await estimateQuality(buffer);
      const targetQuality = Math.min(75, originalQuality);
    
      return sharp(buffer)
        .jpeg({
          quality: Math.round(targetQuality * qualityFactor),
          mozjpeg: true,
        })
        .toBuffer();
    }

    为什么要评估原图质量?

    在图片压缩过程中,评估原图质量是一个非常重要的步骤,主要有以下几个原因:

    1. 避免过度压缩
    • 如果原图已经经过压缩(比如质量值为 60),再使用更高的质量值(如 75)进行压缩反而会增加文件大小
    • 通过评估原图质量,我们可以确保压缩后的质量不会超过原图,避免不必要的文件体积增加
    1. 智能质量控制
    • JPEG 图片的质量值范围是 0-100
    • 通过分析量化表(Quantization Tables),我们可以估算出原图的压缩质量
    • 这样可以根据原图质量动态调整压缩参数,而不是简单地使用固定值
    1. 保持图片质量平衡
    • 对于已经高度压缩的图片,继续压缩可能会导致明显的质量损失
    • 通过评估原图质量,我们可以在文件大小和视觉质量之间找到更好的平衡点

    质量评估的实现原理

    我们通过分析 JPEG 文件的量化表来估算图片质量:

    1. 基准量化表
    • 使用 JPEG 标准中定义的基准亮度量化表(质量值 50)
    1. 比较算法
    • 提取图片中的量化表
    • 将实际量化表与基准表进行比较
    • 计算缩放因子并使用经验公式估算质量值
    1. 质量计算
    • 当缩放因子小于 1 时,表示质量低于 50
    • 当缩放因子大于 1 时,表示质量高于 50
    • 最终结果被限制在 0-100 范围内

    这种方法让我们能够智能地控制压缩过程,避免压缩后文件变大的问题。

    PNG 压缩策略

    PNG 格式常用于需要透明度的图像和图标,压缩策略如下:

    • 使用最高压缩级别(level 9)
    • 启用调色板模式

    为什么使用调色板模式?

    调色板模式(Palette Mode)是 PNG 格式中一种重要的优化技术,具有以下优势:

    1. 数据存储优化
    • 传统 PNG 使用 RGB/RGBA 模式,每个像素需要 3-4 个字节
    • 调色板模式将颜色信息存储在一个查找表中
    • 每个像素只需要一个索引值(通常是 1 个字节)来引用颜色表
    1. 适用场景
    • 对于颜色数量有限的图像(如图标、Logo)效果最佳
    • 当图像中重复颜色较多时,可显著减小文件大小
    • 特别适合扁平化设计的 UI 元素
    1. 压缩效率
    • 减少了像素数据的存储空间
    • 提高了 DEFLATE 算法的压缩效率
    • 可以在保持视觉质量的同时大幅减小文件体积
    1. 智能转换
    • Sharp 库会自动评估是否适合使用调色板模式
    • 如果颜色过多,会自动降级到标准 RGB/RGBA 模式
    • 确保最终输出的图像质量

    通用优化策略

    对于所有图片格式,我们都采用以下通用策略:

    1. 阈值控制
    • 设置最小压缩阈值(50KB)
    • 只在压缩效果显著时才保存
    • 避免过度压缩
    1. 性能优化
    • 使用 Utility Process 处理压缩任务
    • 根据 CPU 核心数控制并发
    • 批量处理时分批执行

    并发控制实现

    在处理大量图片时,我们使用 p-limit 库来控制并发任务数量。具体实现如下:

    1. 动态并发数设置
    // 根据 CPU 核心数确定并发数
    const concurrency = Math.max(os.cpus().length - 1, 1);
    const limit = pLimit(concurrency);
    1. 任务队列管理
    // 将所有图片压缩任务映射为限制并发的 Promise
    const tasks = images.map((image) =>
      limit(async () => {
        try {
          const imageBuffer = await readFile(image);
          // ... 压缩处理逻辑 ...
    
          completed++;
          // 更新进度
          const progressPercentage = Math.round((completed / totalImages) * 100);
          process.parentPort.postMessage({
            type: 'loading',
            payload: {
              progress: `${progressPercentage}%`,
            },
          });
    
          return sizeReduction;
        } catch (error) {
          console.error(`Error processing ${image}:`, error);
          return 0;
        }
      })
    );

    总结

    总的来说,我们在怡氧桌面端里做了个挺实用的功能 – Office 文档图片压缩。这个功能直接帮用户解决了文档太大、打开慢的问题,让大家协作起来更顺畅了。

    参考链接

    1. Sharp – High performance Node.js image processing
    2. Electron Utility Process – 实用程序进程
    3. p-limit – Run multiple promise-returning & async functions with limited concurrency