使用nodejs為圖片添加全頁面半透明水印

業(yè)務(wù)背景

作為中后臺項目的導(dǎo)出功能赖淤,通常會被要求具備導(dǎo)出的追溯能力管搪。

當導(dǎo)出的數(shù)據(jù)形態(tài)為圖片時谆构,一般會為圖片添加水印以達到此目的裸扶。

DEMO

那么在導(dǎo)出圖片前如何為其添加上可以作為導(dǎo)出者身份識別的水印呢?先看成品:

demo.jpeg

上圖原圖為我隨便在網(wǎng)上找的一張圖片搬素,添加水印之后的效果如圖所示呵晨。

業(yè)務(wù)需求分解

這里我們需要考慮在此業(yè)務(wù)場景之下,這個需求的三個要點:

  • 水印需要鋪滿整個圖片

  • 水印文字成半透明狀蔗蹋,保證原圖的可讀性

  • 水印文字應(yīng)清晰可讀

選型

如我一樣負責(zé)在一個nodejs server上實現(xiàn)以上需求何荚,可選項相當多,比如直接使用c lib imagemagick或者已有人封裝的各種node watermarking庫猪杭。在本文中餐塘,我們將選擇使用對Jimp庫的封裝。

Jimp 庫的官方github頁面上這樣描述它自己:

An image processing library for Node written entirely in JavaScript, with zero native dependencies.

并且提供為數(shù)眾多的操作圖片的API

  • blit - Blit an image onto another.
  • blur - Quickly blur an image.
  • color - Various color manipulation methods.
  • contain - Contain an image within a height and width.
  • cover - Scale the image so the given width and height keeping the aspect ratio.
  • displace - Displaces the image based on a displacement map
  • dither - Apply a dither effect to an image.
  • flip - Flip an image along it's x or y axis.
  • invert - Invert an images colors
  • mask - Mask one image with another.
  • normalize - Normalize the colors in an image
  • print - Print text onto an image
  • scale - Uniformly scales the image by a factor.

在本文所述的業(yè)務(wù)場景中皂吮,我們只需使用其中部分API即可戒傻。

設(shè)計和實現(xiàn)

input 參數(shù)設(shè)計:

  • url: 原圖片的存儲地址(對于Jimp來說,可以是遠程地址蜂筹,也可以是本地地址)

  • textSize: 需添加的水印文字大小

  • opacity:透明度

  • text:需要添加的水印文字

  • dstPath:添加水印之后的輸出圖片地址需纳,地址為腳本執(zhí)行目錄的相對路徑

  • rotate:水印文字的旋轉(zhuǎn)角度

  • colWidth:因為可旋轉(zhuǎn)的水印文字是作為一張圖片覆蓋到原圖上的,因此這里定義一下水印圖片的寬度艺挪,默認為300像素

  • rowHeight:理由同上不翩,水印圖片的高度,默認為50像素。(PS:這里的水印圖片尺寸可以大致理解為水印文字的間隔)

因此在該模塊的coverTextWatermark函數(shù)中對外暴露以上參數(shù)即可

coverTextWatermark

/**
 * @param {String} mainImage - Path of the image to be watermarked
 * @param {Object} options
 * @param {String} options.text     - String to be watermarked
 * @param {Number} options.textSize - Text size ranging from 1 to 8
 * @param {String} options.dstPath  - Destination path where image is to be exported
 * @param {Number} options.rotate   - Text rotate ranging from 1 to 360
 * @param {Number} options.colWidth - Text watermark column width
 * @param {Number} options.rowHeight- Text watermark row height
 */

module.exports.coverTextWatermark = async (mainImage, options) => {
  try {
    options = checkOptions(options);
    const main = await Jimp.read(mainImage);
    const watermark = await textWatermark(options.text, options);
    const positionList = calculatePositionList(main, watermark)
    for (let i =0; i < positionList.length; i++) {
      const coords = positionList[i]
      main.composite(watermark,
        coords[0], coords[1] );
    }
    main.quality(100).write(options.dstPath);
    return {
      destinationPath: options.dstPath,
      imageHeight: main.getHeight(),
      imageWidth: main.getWidth(),
    };
  } catch (err) {
    throw err;
  }
}

textWatermark

Jimp不能直接將文本旋轉(zhuǎn)一定角度口蝠,并寫到原圖片上器钟,因此我們需要根據(jù)水印文本生成新的圖片二進制流,并將其旋轉(zhuǎn)妙蔗。最終以這個新生成的圖片作為真正的水印添加到原圖片上傲霸。下面是生成水印圖片的函數(shù)定義:

const textWatermark = async (text, options) => {
  const image = await new Jimp(options.colWidth, options.rowHeight, '#FFFFFF00');
  const font = await Jimp.loadFont(SizeEnum[options.textSize])
  image.print(font, 10, 0, {
    text,
    alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER,
    alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE
  },
  400,
  50)
  image.opacity(options.opacity);
  image.scale(3)
  image.rotate( options.rotation )
  image.scale(0.3)
  return image
}

calculatePositionList

到目前為止原圖有了,水印圖片也有了眉反,如果想達到鋪滿原圖的水印效果昙啄,我們還需要計算出水印圖片應(yīng)該在哪些坐標上畫在原圖上,才能達成水印鋪滿原圖的目的寸五。

const calculatePositionList = (mainImage, watermarkImg) => {
  const width = mainImage.getWidth()
  const height = mainImage.getHeight()
  const stepWidth = watermarkImg.getWidth()
  const stepHeight = watermarkImg.getHeight()
  let ret = []
  for(let i=0; i < width; i=i+stepWidth) {
    for (let j = 0; j < height; j=j+stepHeight) {
      ret.push([i, j])
    }
  }
  return ret
}

如上代碼所示梳凛,我們使用一個二維數(shù)組記錄所有水印圖片需出現(xiàn)在原圖上的坐標列表。

總結(jié)

至此梳杏,關(guān)于使用Jimp為圖片添加文字水印的所有主要功能就都講解到了伶跷。

github地址:https://github.com/swearer23/jimp-fullpage-watermark

npm:npm i jimp-fullpage-watermark

Inspiration 致謝

https://github.com/sushantpaudel/jimp-watermark

https://github.com/luthraG/image-watermark

Image Processing in NodeJS with Jimp - Medium

示例代碼:

var watermark = require('jimp-fullpage-watermark');

watermark.coverTextWatermark('./img/main.jpg', {
  textSize: 5,
  opacity: 0.5,
  rotation: 45,
  text: 'watermark test',
  colWidth: 300,
  rowHeight: 50
}).then(data => {
    console.log(data);
}).catch(err => {
    console.log(err);
});
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市秘狞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蹈集,老刑警劉巖烁试,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異拢肆,居然都是意外死亡减响,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門郭怪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來支示,“玉大人,你說我怎么就攤上這事鄙才∷毯瑁” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵攒庵,是天一觀的道長嘴纺。 經(jīng)常有香客問我,道長浓冒,這世上最難降的妖魔是什么栽渴? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮稳懒,結(jié)果婚禮上闲擦,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好墅冷,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布纯路。 她就那樣靜靜地躺著,像睡著了一般俺榆。 火紅的嫁衣襯著肌膚如雪感昼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天罐脊,我揣著相機與錄音定嗓,去河邊找鬼。 笑死萍桌,一個胖子當著我的面吹牛宵溅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播上炎,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼恃逻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了藕施?” 一聲冷哼從身側(cè)響起寇损,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎裳食,沒想到半個月后矛市,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡诲祸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年浊吏,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片救氯。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡找田,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出着憨,到底是詐尸還是另有隱情墩衙,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布甲抖,位于F島的核電站底桂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏惧眠。R本人自食惡果不足惜籽懦,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望氛魁。 院中可真熱鬧暮顺,春花似錦厅篓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惫恼,卻和暖如春档押,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背祈纯。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工令宿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人腕窥。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓粒没,卻偏偏與公主長得像,于是被迫代替她去往敵國和親簇爆。 傳聞我的和親對象是個殘疾皇子癞松,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容