欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java實(shí)現(xiàn)多行文字水印的方法詳解

 更新時間:2023年02月06日 14:09:32   作者:遛馬少年  
這篇文章主要為大家詳細(xì)介紹了如何利用Java實(shí)現(xiàn)多行文字水印的方法,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,需要的可以參考一下

一、業(yè)務(wù)場景

公司處方藥品銷售業(yè)務(wù),需要在線開具處方或者手動上傳處方圖片,處方圖片在購藥使用之后需要添加已使用的水印字樣,防止處方圖片的重復(fù)使用。因此,需要設(shè)計一個為圖片添加文字水印的Java工具類,針對這個問題,我展開了解決方案的研究。

1.1 UI設(shè)計的水印原型

二、實(shí)現(xiàn)方案探索

2.1 準(zhǔn)備處方圖片

我們先在互聯(lián)網(wǎng)醫(yī)院,給患者“程咬金”開個處方單

2.2 單行文字水印

首先是翻找項(xiàng)目中通用工具類,發(fā)現(xiàn)有一個WaterMarkUtils的類,應(yīng)該就是給圖片添加水印的工具類

/** 
* @author 遛馬少年 
* @Title WaterMarkUtils
 * @Description
 */
public class WaterMarkUtils {

   /**
    * 圖片添加水印
    *
    * @param imgFile
    *            需要添加水印的圖片
    * @param markContentColor
    *            水印文字的顏色
    * @param waterMarkContent
    *            水印的文字
    * @return  水印圖片
    */
   public static File markStr(File imgFile, Color markContentColor, String waterMarkContent) {
      try {
         // 加水印
         BufferedImage bufImg = ImageIO.read(imgFile);
         int width = bufImg.getWidth(); //圖片寬
         int height = bufImg.getHeight(); //圖片高
         Graphics2D g = bufImg.createGraphics();
         g.drawImage(bufImg, 0, 0, width, height, null);
         Font font = new Font("微軟雅黑", Font.ITALIC, 45);
         g.setColor(markContentColor); // 根據(jù)圖片的背景設(shè)置水印顏色

         g.setFont(font);
         int x = width -2*getWatermarkLength(waterMarkContent, g); //這是一個計算水印位置的函數(shù),可以根據(jù)需求添加
         int y = height - 1*getWatermarkLength(waterMarkContent, g);
         g.drawString(waterMarkContent, x, y);
         g.dispose();

         ImageIO.write(bufImg, "png", imgFile);
         return imgFile;
      } catch (Exception e) {
          log.error("WaterMarkUtils-markStr err:{} ",e.getMessage());
      }
      return null;
   }

   /**
    * 獲取水印文字總長度
    *
    * @param waterMarkContent
    *            水印的文字
    * @param g
    * @return 水印文字總長度
    */
   public static int getWatermarkLength(String waterMarkContent, Graphics2D g) {
      return g.getFontMetrics(g.getFont()).charsWidth(waterMarkContent.toCharArray(), 0, waterMarkContent.length());
   }
}

寫個main方法試一下效果

public class TestWaterMark {   public static void main(String[] args) {      String rxPath = "F:\Temp\rxImage\程咬金.png";
      File rxFile = new File(rxPath);
      WaterMarkUtils.markStr(rxFile, Color.RED, "該處方已使用,處方僅允許在本平臺進(jìn)行使用,用戶在其他平臺使用本處方引起的糾紛概不負(fù)責(zé)");
   }

看看效果:

只在中間位置插入了一行水印,并且因?yàn)樗∥淖痔L而顯示不全,顯然是不符合要求的。

2.3 兩行水印方案

接著,基于上面的水印方案,做了點(diǎn)改造

首先將根據(jù)圖片大小計算字體大小,添加文字水印的本質(zhì)是基于Graphics2D在圖片上進(jìn)行繪制操作,要實(shí)現(xiàn)多行水印,就要循環(huán)遍歷圖片的行級像素,然后添加文字即可,最終代碼如下

/** 
* @author 遛馬少年 
* @Title ImageWatermarkUtil 
* @Description
 */
@Slf4j
public class ImageWatermarkUtil {
   // 水印透明度
   private static float alpha = 0.1f;
   // 水印文字顏色
   private static Color color = Color.RED;
   // 水印之間的間隔
   private static final int XMOVE = 80;
   // 水印之間的間隔
   private static final int YMOVE = 80;

   /**
    * 獲取文本長度。漢字為1:1,英文和數(shù)字為2:1
    */
   private static int getTextLength(String text) {
      int length = text.length();
      for (int i = 0; i < text.length(); i++) {
         String s = String.valueOf(text.charAt(i));
         if (s.getBytes().length > 1) {
            length++;
         }
      }
      length = length % 2 == 0 ? length / 2 : length / 2 + 1;
      return length;
   }

   /**
    * 給圖片添加水印文字、可設(shè)置水印文字的旋轉(zhuǎn)角度
    *
    * @param srcImgPath   原圖片路徑
    * @param dstImgPath   加完水印之后圖片路徑
    * @param degree       旋轉(zhuǎn)角度
    * @param logoText     水印主標(biāo)題
    * @param logoTextPlus 水印副標(biāo)題
    */
   public static void ImageByText(String srcImgPath, String dstImgPath, Integer degree, String logoText, String logoTextPlus) {
      File srcFile = new File(srcImgPath);
      File dstFile = new File(dstImgPath);
      ImageByText(srcFile, dstFile, degree, logoText, logoTextPlus);
   }

   public static void ImageByText(File srcFile, File dstFile, Integer degree, String logoText, String logoTextPlus) {

      InputStream is = null;
      OutputStream os = null;
      try {
         long start = System.currentTimeMillis();
         // 源圖片
         Image srcImg = ImageIO.read(srcFile);
         int width = srcImg.getWidth(null);// 原圖寬度
         int height = srcImg.getHeight(null);// 原圖高度
         BufferedImage buffImg = new BufferedImage(srcImg.getWidth(null), srcImg.getHeight(null),
               BufferedImage.TYPE_INT_RGB);
         // 得到畫筆對象
         Graphics2D g = buffImg.createGraphics();
         // 設(shè)置對線段的鋸齒狀邊緣處理
         g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
         g.drawImage(srcImg.getScaledInstance(srcImg.getWidth(null), srcImg.getHeight(null), Image.SCALE_SMOOTH),
               0, 0, null);
         // 設(shè)置水印旋轉(zhuǎn)
         if (null != degree) {
            g.rotate(Math.toRadians(degree), (double) buffImg.getWidth() / 2, (double) buffImg.getHeight() / 2);
         }
         int txtLen = logoTextPlus.length();
         int FONT_SIZE = width / txtLen;
         Font font = new Font("微軟雅黑", Font.BOLD, FONT_SIZE);
         // 設(shè)置水印文字顏色
         g.setColor(color);
         // 設(shè)置水印文字Font
         g.setFont(font);
         // 設(shè)置水印文字透明度
         g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
         int x = -width / 2;
         int y = -height / 2;
         int markWidth = FONT_SIZE * txtLen;// 字體長度
         int markHeight = FONT_SIZE;// 字體高度

         // 循環(huán)添加水印
         while (x < width * 1.5) {
            y = -height / 2;
            while (y < height * 1.5) {
               g.drawString(logoText, x, y);
               y += markHeight + YMOVE;
               g.drawString(logoTextPlus, x, y);
               y += markHeight + YMOVE;
            }
            x += markWidth + XMOVE;
         }
         // 釋放資源
         g.dispose();
         // 生成圖片
         os = new FileOutputStream(dstFile);
         ImageIO.write(buffImg, FileUtil.extName(srcFile), os);
         long time = System.currentTimeMillis() - start;
         log.info("添加水印文字成功!耗時(ms):{}", time);
      } catch (Exception e) {
         e.printStackTrace();
      } finally {
         try {
            if (null != os)
               os.close();
         } catch (Exception e) {
            e.printStackTrace();
         }
      }
   }
}

寫個main方法,試一下效果

public class TestWaterMark {   public static void main(String[] args) {      String rxPath = "F:\Temp\rxImage\程咬金.png";      File rxFile = new File(rxPath);
      String rxDstPath = "F:\Temp\rxImage\程咬金-加水印.png";
      File rxDstFile = new File(rxDstPath);
      ImageWatermarkUtil.ImageByText(rxFile, rxDstFile, -40, "該處方已使用", "處方僅允許在本平臺進(jìn)行使用,用戶在其他平臺使用本處方引起的糾紛概不負(fù)責(zé)");
   }
}

效果如下:

已經(jīng)很接近原型設(shè)計了,為了實(shí)現(xiàn)多行效果,我在循環(huán)里面調(diào)用了兩次drawString

g.drawString(logoText, x, y);
g.drawString(logoTextPlus, x, y);

但是還有問題,就是第二行文字太長,并不能完整地顯示,追求完美的我沒有放棄,接著改造

2.4 多行水印方案

既然第二行太長,那就再把第二行拆分,每行幾個字即可,然后再循環(huán)寫文字

最終代碼如下

/** 
* @author 遛馬少年 
* @Title ImageWatermarkUtil2 
* @Description */
public class ImageWatermarkUtil2 {
   // 水印透明度
   private static float alpha = 0.1f;
   private static int fontSize = 80;
   // 水印文字字體
   private static Font font = new Font("微軟雅黑", Font.BOLD, fontSize);
   // 水印文字顏色
   private static Color color = Color.RED;

   /**
    * 水印之間的橫向間隔
    */
   private static final int XMOVE = 80;

   /**
    * 水印之間的縱向間隔
    */
   private static final int YMOVE = 80;


   /**
    * 給圖片添加水印文字、可設(shè)置水印文字的旋轉(zhuǎn)角度
    *
    * @param logoText 水印文字
    * @param srcFile  源文件
    * @param dstFile  輸出文件
    * @param degree   設(shè)置角度
    */
   public static void markImageByText(File srcFile, File dstFile, Integer degree, String logoText) {
      long start = System.currentTimeMillis();
      InputStream is = null;
      try {
         String[] waterMarkContents = logoText.split("\|\|");
         // 1、源圖片
         Image srcImg = ImageIO.read(srcFile);
         // 原圖寬度
         int srcImgWidth = srcImg.getWidth(null);
         // 原圖高度
         int srcImgHeight = srcImg.getHeight(null);
         BufferedImage buffImg = new BufferedImage(srcImg.getWidth(null),
               srcImg.getHeight(null), BufferedImage.TYPE_INT_RGB);
         // 2、得到畫筆對象
         Graphics2D g = buffImg.createGraphics();
         // 3、設(shè)置對線段的鋸齒狀邊緣處理
         g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
         g.drawImage(
               srcImg.getScaledInstance(srcImg.getWidth(null),
                     srcImg.getHeight(null), Image.SCALE_SMOOTH), 0, 0,
               null);
         // 4、設(shè)置水印旋轉(zhuǎn)
         if (null != degree) {
            g.rotate(Math.toRadians(degree),
                  (double) buffImg.getWidth() / 2,
                  (double) buffImg.getHeight() / 2);
         }
         // 5、設(shè)置水印文字顏色
         g.setColor(color);
         // 6、設(shè)置水印文字Font
         g.setFont(font);
         // 7、設(shè)置水印文字透明度
         g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,
               alpha));
         // 8、第一參數(shù)->設(shè)置的內(nèi)容,后面兩個參數(shù)->文字在圖片上的坐標(biāo)位置(x,y)
         // 獲取其中最長的文字水印的大小
         int maxLen = 0;
         int maxHigh = 0;
         String waterMarkContent = "";
         for (int i = 0; i < waterMarkContents.length; i++) {
            waterMarkContent = waterMarkContents[i];
            int fontLen = getWatermarkLength(waterMarkContent, g);
            if (fontLen >= maxLen) {
               maxLen = fontLen;
            }
            maxHigh = maxHigh + (i + 1) * fontSize + 10;
         }
         // 文字長度相對于圖片寬度應(yīng)該有多少行
         int line = srcImgWidth * 2 / maxLen;
         int co = srcImgHeight * 2 / maxHigh;

         int yz = 0;
         // 填充Y軸方向
         for (int a = 0; a < co; a++) {
            int t = 0;
            for (int j = 0; j < waterMarkContents.length; j++) {
               waterMarkContent = waterMarkContents[j];
               int y = (j + 1) * fontSize + 10 + t;

               // 文字疊加,自動換行疊加,注意符號
               int tempX = -srcImgWidth / 2;
               int tempY = -srcImgHeight / 2 + y + yz;
               // 單字符長度
               int tempCharLen = 0;
               // 單行字符總長度臨時計算
               int tempLineLen = 0;
               StringBuffer sb = new StringBuffer();
               for (int i = 0; i < waterMarkContent.length(); i++) {
                  char tempChar = waterMarkContent.charAt(i);
                  tempCharLen = getCharLen(tempChar, g);
                  tempLineLen += tempCharLen;

                  // 和圖片的長度進(jìn)行對應(yīng)的比較操作
                  if (tempLineLen >= srcImgWidth) {
                     // 長度已經(jīng)滿一行,進(jìn)行文字疊加
                     g.drawString(sb.toString(), tempX, tempY);
                     t = t + fontSize;
                     // 清空內(nèi)容,重新追加
                     sb.delete(0, sb.length());
                     tempY += fontSize;
                     tempLineLen = 0;
                  }
                  // 追加字符
                  sb.append(tempChar);
               }
               // 填充X軸
               for (int z = 0; z < line; z++) {
                  // 最后疊加余下的文字
                  g.drawString(sb.toString(), tempX, tempY);
                  tempX = tempX + maxLen + XMOVE;
               }
            }
            yz = yz + maxHigh + YMOVE;
         }
         // 9、釋放資源
         g.dispose();
         // 10、生成圖片
         ImageIO.write(buffImg, "png", new FileOutputStream(dstFile));
         System.out.println("圖片完成添加水印文字,耗時:{}" + (System.currentTimeMillis() - start));
      } catch (Exception e) {
         e.printStackTrace();
      } finally {
         try {
            if (null != is)
               is.close();
         } catch (Exception e) {
            e.printStackTrace();
         }
      }
   }


   public static int getCharLen(char c, Graphics2D g) {
      return g.getFontMetrics(g.getFont()).charWidth(c);
   }

   /**
    * 獲取水印文字總長度
    *
    * @paramwaterMarkContent水印的文字
    * @paramg
    * @return水印文字總長度
    */
   public static int getWatermarkLength(String waterMarkContent, Graphics2D g) {
      return g.getFontMetrics(g.getFont()).charsWidth(waterMarkContent.toCharArray(), 0, waterMarkContent.length());
   }
}

再試下最終效果

public class TestWaterMark {   public static void main(String[] args) {      String rxPath = "F:\Temp\rxImage\程咬金.png";      File rxFile = new File(rxPath);      String rxDstPath = "F:\Temp\rxImage\程咬金-加水印.png";
      File rxDstFile = new File(rxDstPath);
      ImageWatermarkUtil2.markImageByText(rxFile, rxDstFile, -40, "該處方已使用||本處方僅允許在本平臺進(jìn)行使用,||用戶在其他平臺使用本處方引起的||糾紛概不負(fù)責(zé)");
   }
}

效果如下:

中間空白位置有點(diǎn)多,其實(shí)可以通過修改循環(huán)y的步長來控制

Anyway,這個效果最終滿足了UI驗(yàn)收要求

三、總結(jié)

多行水印實(shí)現(xiàn)起來,總體還是比較簡單的。唯一的難點(diǎn)的多行的實(shí)現(xiàn),目前網(wǎng)上搜索到的解決方案,大多都是單行的。多行的實(shí)現(xiàn)其實(shí)就是基于單行,多了一層循環(huán)遍歷。

有個不太確定的點(diǎn)是,添加水印的字體大小是否需要根據(jù)圖片大小動態(tài)調(diào)整,比如圖片較大時,水印文字字體也跟著變大。所以我的第二個方案是動態(tài)調(diào)整的,最終方案又是規(guī)定大小的。

至于怎么設(shè)計,看各位的實(shí)際業(yè)務(wù)需求。

到此這篇關(guān)于Java實(shí)現(xiàn)多行文字水印的方法詳解的文章就介紹到這了,更多相關(guān)Java多行文字水印內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java環(huán)境中MyBatis與Spring或Spring MVC框架的集成方法

    Java環(huán)境中MyBatis與Spring或Spring MVC框架的集成方法

    和MyBatis類似,Spring或者Spring MVC框架在Web應(yīng)用程序的運(yùn)作中同樣主要負(fù)責(zé)處理數(shù)據(jù)庫事務(wù),這里我們就來看一下Java環(huán)境中MyBatis與Spring或Spring MVC框架的集成方法
    2016-06-06
  • java?CompletableFuture異步任務(wù)編排示例詳解

    java?CompletableFuture異步任務(wù)編排示例詳解

    這篇文章主要為大家介紹了java?CompletableFuture異步任務(wù)編排示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • 一文學(xué)會使用sa-token解決網(wǎng)站權(quán)限驗(yàn)證

    一文學(xué)會使用sa-token解決網(wǎng)站權(quán)限驗(yàn)證

    這篇文章主要為大家介紹了使用sa-token解決網(wǎng)站權(quán)限驗(yàn)證方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • java中添加按鈕并添加響應(yīng)事件的方法(推薦)

    java中添加按鈕并添加響應(yīng)事件的方法(推薦)

    下面小編就為大家?guī)硪黄猨ava中添加按鈕并添加響應(yīng)事件的方法(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • 深入了解Spring中Bean的作用域和生命周期

    深入了解Spring中Bean的作用域和生命周期

    這篇文章主要介紹了深入了解Spring中Bean的作用域和生命周期,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10
  • springboot整合 beatlsql的實(shí)例代碼

    springboot整合 beatlsql的實(shí)例代碼

    這篇文章主要介紹了springboot整合 beatlsql的實(shí)例代碼,BeetSql是一個全功能DAO工具,同時具有hibernate 優(yōu)點(diǎn) & Mybatis優(yōu)點(diǎn)功能,有興趣的可以了解一下
    2017-05-05
  • LongAdder原理及創(chuàng)建使用示例詳解

    LongAdder原理及創(chuàng)建使用示例詳解

    這篇文章主要為大家介紹了LongAdder原理及創(chuàng)建使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • java各種類型對象占用內(nèi)存情況分析

    java各種類型對象占用內(nèi)存情況分析

    這篇文章主要介紹了java各種類型對象占用內(nèi)存情況分析,對內(nèi)存或者性能優(yōu)化感興趣的同學(xué),一定要看一下
    2021-04-04
  • spring?webflux響應(yīng)式編程使用詳解

    spring?webflux響應(yīng)式編程使用詳解

    webflux,即響應(yīng)式編程,響應(yīng)式編程是一種用于處理異步數(shù)據(jù)流和事件的編程范式,spring?webflux是spring在5.0版本后提供的一套響應(yīng)式編程風(fēng)格的web開發(fā)框架,本文給大家詳細(xì)講講spring?webflux響應(yīng)式編程的使用,需要的朋友可以參考下
    2023-10-10
  • Java工程中可執(zhí)行JAR兩種打包方式詳解

    Java工程中可執(zhí)行JAR兩種打包方式詳解

    這篇文章主要為大家詳細(xì)介紹了Java工程中可執(zhí)行JAR兩種打包方式,一體化可執(zhí)行包和帶外部依賴lib的可執(zhí)行包,有需要的小伙伴可以學(xué)習(xí)一下
    2024-04-04

最新評論