When a Java mobile phone uploads a picture for watermarking, the picture is flipped 90 degrees.

Problem background:

When uploading pictures on the mobile phone and printing watermarks in the background, it was found that the pictures were flipped 90 degrees, and the picture upload in the window was indeed displayed normally.

Solution:

1. Reference dependencies:

<dependency>
   <groupId>com.drewnoakes</groupId>
   <artifactId>metadata-extractor</artifactId>
   <version>2.7.2</version>
</dependency>

2. Obtain the flip angle of the image, correct it, add a watermark, and upload it to the file server

/**
 * Get the image rotation angle (photos uploaded on the mobile phone)
 *
 * @param inputStream image stream
 * @return
 */
private static int getAngle(InputStream inputStream) throws Exception {
    Metadata metadata = ImageMetadataReader.readMetadata(inputStream);
    for (Directory directory : metadata.getDirectories()) {
        for (Tag tag : directory.getTags()) {
            if ("Orientation".equals(tag.getTagName())) {
                String orientation = tag.getDescription();
                if (orientation.contains("90")) {
                    return 90;
                } else if (orientation.contains("180")) {
                    return 180;
                } else if (orientation.contains("270")) {
                    return 270;
                }
            }
        }
    }
    return 0;
} 
@Override
public String setWaterMarkAndUpload(MultipartFile file, String type, String address) throws IOException, NoSuchAlgorithmException {
    if (ObjectUtils.isEmpty(type)) {
        throw new BusinessException("Type cannot be null!");
    }

    // Get redis watermark configuration information
    String key = RedisKeyConstant.wx_citizen_watermark_key + ":" + type;
    WxCitizenSettingWatermarkVO settingWatermarkVO = BeanUtil.toBean(JSONUtil.parse(redisService.get(key)), WxCitizenSettingWatermarkVO.class);
    if (ObjectUtils.isEmpty(settingWatermarkVO)) {
        throw new BusinessException("System watermark setting not found!");
    }
    if (settingWatermarkVO.getDbStatus().equals("2")) {
        String fileName = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf("."));
        String url = this.upload(file, fileName, "1", null);
        return url;
    }

    if (ObjectUtil.isNotEmpty(settingWatermarkVO.getWmWord()) & amp; & amp; settingWatermarkVO.getWmWord().equals("1")) {
        if (ObjectUtil.isEmpty(address)) {
            throw new BusinessException("Address cannot be empty!");
        }
    }

    BufferedImage bufImg = null;
    try {
        //File full name + suffix
        String originalFileName = file.getOriginalFilename();
        String fileName = originalFileName.substring(0, originalFileName.lastIndexOf("."));
        //Suffix name
        String suffix = originalFileName.substring(originalFileName.lastIndexOf(".") + 1);
        //Determine whether the file type is an image
        String[] imgType = {"bmp", "jpg", "jpeg", "png", "gif"};
        List<String> stringList = Arrays.asList(imgType);
        if (!stringList.contains(suffix.toLowerCase())) {
            throw new BusinessException("Wrong file type, please select a picture type file");
        }
        //Data flow conversion storage (to prevent flow failure)
        InputStream inputStream = file.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024 * 1024];
        int len;
        while ((len = inputStream.read(buffer)) > -1) {
            baos.write(buffer, 0, len);
        }
        baos.flush();
        // Get the image rotation angle
        InputStream metadataInputStream = new ByteArrayInputStream(baos.toByteArray());
        int angle = getAngle(metadataInputStream);


        //Get the source image bytes (for use when watermark is not turned on)
        byte[] imgBuffer = baos.toByteArray();
        InputStream stream = new ByteArrayInputStream(baos.toByteArray());
        //Get the source image (for use when turning on watermark)
        Image srcImg = ImageIO.read(stream);

        //Whether the wrong picture needs to be corrected
        boolean isCorrectImg = false;
        //Source image width and height
        int imgWidth = 0;
        int imgHeight = 0;
        if (angle == 90 || angle == 270) {
            isCorrectImg = true;
            imgWidth = srcImg.getHeight(null);
            imgHeight = srcImg.getWidth(null);
        } else {
            isCorrectImg = false;
            imgWidth = srcImg.getWidth(null);
            imgHeight = srcImg.getHeight(null);
        }

        // horizontal offset
        int devLevel = 0;
        // vertical offset
        int devVertical = 0;
        // watermark transparency
        float alpha = 1f;
        //Watermark horizontal position
        int positionWidth = 10;
        //Watermark vertical position
        int positionHeight = 20;
        //Watermark text color
        String colorText = settingWatermarkVO.getColor();
        final String[] colors = colorText.replace("rgb", "").replace("(", "").replace(")", "").replace(" ", "").split(" ,");
        List<String> strings = Arrays.asList(colors);
        List<Integer> collect = strings.stream().map(p -> Integer.valueOf(p)).collect(Collectors.toList());
        Color color = new Color(collect.get(0), collect.get(1), collect.get(2));

        //Format time
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = simpleDateFormat.format(new Date());
        //Text to be printed
        List<String> texts = new ArrayList<>();
        //watermark text
        switch (settingWatermarkVO.getWmWord()) {
            case "1": // address + time
                //Remove the address and ignore the content
                if (ObjectUtil.isNotEmpty(settingWatermarkVO.getAddressIgnoreText())) {
                    address = address.replace(settingWatermarkVO.getAddressIgnoreText(), "");
                }
                texts.add(address);
                texts.add(time);
                break;
            case "2": // time
                texts.add(time);
                break;
            default:
                throw new BusinessException("Wrong text type");
        }
        bufImg = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
        //Watermark text font
        Font font = new Font("宋体", Font.BOLD, settingWatermarkVO.getSize());
        //Add text watermark
        mark(bufImg, srcImg, texts, font, color, imgWidth, imgHeight, settingWatermarkVO.getImgPosition(), isCorrectImg, angle);
        //Image flow byte stream
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ImageIO.write(bufImg, suffix, out);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(out.toByteArray());
        out.close();
        //Upload to file server
        String url = this.upload(byteArrayInputStream, file.getContentType(), suffix, fileName, "0");
        System.out.println("url:" + url);
        return url;
    } catch (Exception ex) {
        ex.printStackTrace();
        throw new BusinessException(ex.getMessage());
    } finally {
        if (bufImg != null) {
            bufImg.getGraphics().dispose();
        }
    }

}
/**
 * Add text watermark
 * @param bufImg
 * @param img
 * @param texts
 * @param font
 * @param color
 * @param x
 * @param y
 */
public static void mark(BufferedImage bufImg, Image img, List<String> texts, Font font, Color color, int x, int y, String position, boolean isCorrectImg, int angle) {
    Graphics2D g = bufImg.createGraphics();
    //Create a FontMetrics object
    FontMetrics fm = g.getFontMetrics(font);
    int textHeight = fm.getHeight();
    //Find the longest character in the watermark
    String longest = texts.stream()
            .max(Comparator.comparingInt(String::length))
            .orElse("");
    int textWidth = fm.stringWidth(longest);
    int positionWidth = textWidth;
    int positionHeight = textHeight;
    //Set the location to print
    switch (position) {
        case "upper left":
            positionWidth = 0;
            positionHeight = 0 + textHeight;
            break;
        case "upper right":
            positionWidth = x - textWidth;
            positionHeight = textHeight;
            break;
        case "upper middle":
            positionWidth = (x - textWidth) / 2;
            positionHeight = textHeight;
            break;
        case "center":
            positionWidth = (x - textWidth) / 2;
            positionHeight = (y + textHeight) / 2;
            break;
        case "left center":
            positionWidth = 0;
            positionHeight = (y + textHeight) / 2;
            break;
        case "right center":
            positionWidth = x - textWidth;
            positionHeight = (y + textHeight) / 2;
            break;
        case "lower left":
            positionWidth = 0;
            positionHeight = y - textHeight;
            break;
        case "lower middle":
            positionWidth = (x - textWidth) / 2;
            positionHeight = y - textHeight;
            break;
        case "lower right":
            positionWidth = x - textWidth;
            positionHeight = y - textHeight;
            break;
        default:
            positionWidth = x - textWidth;
            positionHeight = y - textHeight;
            break;
    }

    //When there is a flipping problem in the image uploaded on the mobile phone, correct the position
    if (isCorrectImg) {
        // Center point position
        double centerWidth = ((double) x) / 2;
        double centerHeight = ((double) y) / 2;
        g.rotate(Math.toRadians(angle), centerWidth, centerHeight);
        g.drawImage(img, (x - img.getWidth(null)) / 2, (y-img.getHeight(null)) / 2, null);
        g.rotate(Math.toRadians(-angle), centerWidth, centerHeight);
    } else {
        g.drawImage(img, 0, 0, bufImg.getWidth(), bufImg.getHeight(), null);
    }
    g.setColor(color);
    g.setFont(font);
    g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1f));

    int size = font.getSize();
    for (int i = 0; i < texts.size(); i + + ) {
        g.drawString(texts.get(i), positionWidth, positionHeight-size*i);
    }
    g.dispose();
}