快捷搜索:

Java 操作 Office:POI之word图片处理

系列文章:

一 背景问题

二 一个简单的想法

因为最终是要写入word,所以暂时考虑还是使用XWPFRun.addPicture方法在单元格插入图片。但下一步,我们要在执行插入前,对图片做完所需的处理动作。这里可以考虑ImageIO 和 Graphics,这两个Java中的图片图形处理工具类来实现了。

三 Graphics

3.1 简介

java.awt.Graphics是一个抽象类,根据源码中的文档描述,

Graphics类是所有图形上下文的抽象基类,允许应用程序绘制在各种设备上实现的组件以及屏幕外图像上。

3.2 矩形绘制

在图片中绘制一个矩形,来代表框选区域,通过配合颜色选择(红橙黄),可以起到标示作用。矩形区域,我们按照从左到右的方向,可以简单用起点横坐标:x,起点纵坐标:y,以及宽度width+高度height来确定。当然,其实宽度和高度也可以替换终点横坐标 和 终点纵坐标来标示。

在Graphics中绘制矩形的方法:

public void drawRect(int x, int y, int width, int height)

3.3 多边形绘制

矩形只有四个点,显示中可能需要绘制复杂的多边形,那么上述方法就无法满足了。所以Graphics提供了一个更为通用的绘制多边形方法:

public abstract void drawPolygon(int xPoints[], int yPoints[],
                                     int nPoints);

参数的xPoints,yPoints,nPoints分别代表横坐标数组,纵坐标数组,线段个数;xPoints和yPoints大小必须相同。

这个方法会绘制由 nPoint 个线段定义的多边形,其中前 nPoint - 1 个线段是 1 ≤ i ≤ 时从 (xPoints[i - 1], yPoints[i - 1]) 到 (xPoints[i], yPoints[i]) 的线段。如果最后一个点和第一个点不同,则图形会通过在这两点间绘制一条线段来自动闭合。

除了直接输入坐标,也可以通过传入定义的Polygon类来进行封装,使用drawPolygon(Polygon p)实现绘制:

public void drawPolygon(Polygon p) {
        drawPolygon(p.xpoints, p.ypoints, p.npoints);
    }

3.4 实现示例

/**
* 添加多边形
* @param imgFile
* @return
* @throws Exception
*/
public static BufferedImage addPolygon(String imgFile) throws Exception {
        BufferedImage image = ImageIO.read(new File(imgFile));
        Graphics g = image.getGraphics();

        int px2[]={100,480,470,480,240,100,110,100};
        int py2[]={155,175,185,395,315,185,175,155};

        g.setColor(Color.red);
        g.drawPolygon(px2,py2,8);

        return image;
}

四 处理结果写入word

4.1 参数转换

run.addPicture 接收的参数依次为:图片的 InputStream 流,图片类型,图片名称(非文件名),图片宽度、图片高度。通过这个方法,我们就可以把图片插入到指定的表格中,并设置图片的宽高属性。

对于图片输入,addPicture要求的参数是InputStream,而我们上面的图片处理结果,是BufferedImage。显然是无法直接插入到word的Cell中的。那么该怎么办? 两种方法,要么另寻出路,看是否有图片处理完成的结果是InputStream;要么就是想办法把BufferedImage转成InputStream。

写到这里,我想起了一句话:『当手里有锤子,看什么都像钉子』。这句话本来是说容易养成思维定势,但在现实中,如果我们看到的东西限制在某些场景,例如金属制品,那么当我们的需求是把其他东西固定住时,考虑用手中的锤子把已有的材料加工成钉子来达到目的也未尝不可,尽管可能不是最佳的方法。当然,这种尝试并不是盲目的,而是在目标明确,且对材料和手中的工具有一定了解的情况下。

话说回来,BufferedImage本身也是一种流的结构,那么就存在着转为ImputStream的可能。

4.2 代码实现与示例

这篇文章就给出了解决方法,所以采用了这段代码加以实现:

/**
 * 将BufferedImage转换为InputStream
 * @param image
 * @return
 */
public static InputStream bufferedImageToInputStream(BufferedImage image){
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    try {
        ImageIO.write(image, "png", os);
        InputStream input = new ByteArrayInputStream(os.toByteArray());
        return input;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

通过这种方式,结合Graphics对图片的处理,得到的word导出结果如下图所示:

五 总结

在日常开发中,经常会遇到各种问题。排除不合理需求,解决困难的技术问题,保证项目的按时保质完成也是我们作为开发者的几项重要职责。在解决问题的过程中,明确目标、识别关键问题、搜索能力、问题拆解和解决能力都很重要。所谓的『经验』,就是在日常大大小小的考验过后沉淀下来的各项能力,而不只是经历。这点需要特别注意,与大家共勉。

经验分享 程序员 微信小程序 职场和发展