Implementation of Watermarking Items and Selection of Two Implementation Schemes

Keywords: Front-end Attribute JQuery shell encoding

We propose two solutions for watermarking project
1. Implementation with shadow dom

1. Basic Ideas

By attach Shadow, a shadow root node is generated, and then a watermarking is added under the root node through a circular statement. The position is used to layout the absolute and make it full of containers.

 show me the code:

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        /*AMD. Register as an anonymous module.
        *define([], factory); */
        define([], factory());
    } else if (typeof module === 'object' && module.exports) {
        /*Node. Does not work with strict CommonJS, but
        // only CommonJS-like environments that support module.exports,
        // like Node.*/
        module.exports = factory();

    } else {
        /*Browser globals (root is window)*/
        root['watermark'] = factory();
    }
}(this, function () {

    /*Just return a value to define the module export.*/
    let watermark = {};

    /*Loading watermarking*/
    let loadMark = function () {
        let defaultSettings = {
            watermark_id: 'wm_div_id',          //id of total watermarking
            watermark_prefix: 'mask_div_id',    //id prefix of small watermarking
            watermark_txt: "Test watermarking",             //Watermarking Content
            watermark_x: 20,                     //x-axis coordinates of watermarking starting position
            watermark_y: 40,                     //Y-axis coordinates of watermarking starting position
            watermark_rows: 0,                   //Number of watermarking rows
            watermark_cols: 0,                   //Watermark column number
            watermark_x_space: 100,              //Watermarking x-axis interval
            watermark_y_space: 50,               //Watermarking y-axis interval
            watermark_font: 'Microsoft YaHei',           //Watermark font
            watermark_color: 'black',            //Watermark font color
            watermark_fontsize: '18px',          //Watermark font size
            watermark_alpha: 0.15,               //Watermark transparency, set to greater than or equal to 0.005
            watermark_width: 100,                //Watermark Width
            watermark_height: 100,               //Watermark Length
            watermark_angle: 15,                 //Watermark Tilt Degree
            watermark_parent_selector: null,  //The parent element selector mounted by the watermarking plug-in is hung on the body by default without input.
            need_adapt_screen: false,     // Whether to change the width and font size of each watermarking according to the resolution ratio of the screen
            watermark_width_proportion: 15, // Equivalent scaling-up or scaling-down values for each watermarking width adapting to screen changes
            watermark_fontsize_proportion: 95, // Equivalent scaling-up or scaling-down values for each watermarking font size adapting to screen changes
        };
        let watermark_parent_node = null  //The parent element mounted by the watermarking plug-in is hung on the body by default without input.

        let setting = arguments[0] || {};
        /*Replace defaults with configuration items, similar to jquery.extend*/
        if (arguments.length === 1 && typeof arguments[0] === "object") {
            for (let key in setting) {
                if (setting[key] && defaultSettings[key] && setting[key] === defaultSettings[key]) continue;
                /*veronic: resolution of watermark_angle=0 not in force*/
                else if (setting[key] || setting[key] === 0) defaultSettings[key] = setting[key];
            }
        }

        /* Watermarking Containers */
        if (defaultSettings.watermark_parent_selector) {
            watermark_parent_node = document.querySelector(defaultSettings.watermark_parent_selector)
        } else {
            watermark_parent_node = document.body
        }

        /*If an element exists, remove it*/
        let watermark_element = document.getElementById(defaultSettings.watermark_id);
        if (watermark_element) {
            let _parentElement = watermark_element.parentNode;
            if (_parentElement) {
                _parentElement.removeChild(watermark_element);
            }
        }
        
        /*Getting the Initial Position of Watermark*/
        let page_offsetTop = 0;
        let page_offsetLeft = 0;
        page_offsetTop = watermark_parent_node.offsetTop || 0;
        page_offsetLeft = watermark_parent_node.offsetLeft || 0;
        page_width = watermark_parent_node.offsetWidth - defaultSettings.watermark_width / 2 || 0;
        page_height = (Math.max(watermark_parent_node.offsetHeight, watermark_parent_node.scrollHeight) - defaultSettings.watermark_height / 2) || 0;
        defaultSettings.watermark_x = defaultSettings.watermark_x + page_offsetLeft;
        defaultSettings.watermark_y = defaultSettings.watermark_y + page_offsetTop;

        /*Create a watermarking shell div*/
        let otdiv = document.getElementById(defaultSettings.watermark_id);
        let shadowRoot = null;

        if (!otdiv) {
            otdiv = document.createElement('div');

            /*Create shadow dom*/
            otdiv.id = defaultSettings.watermark_id;
            otdiv.style.pointerEvents = "none";

            /*Determining whether browsers support attachShadow methods*/
            if (typeof otdiv.attachShadow === 'function') {
                shadowRoot = otdiv.attachShadow({mode: 'open'});
            } else if (typeof otdiv.createShadowRoot === 'function') {
                shadowRoot = otdiv.createShadowRoot();
            } else {
                shadowRoot = otdiv;
            }

            watermark_parent_node.appendChild(otdiv)

        } else if (otdiv.shadowRoot) {
            shadowRoot = otdiv.shadowRoot;
        }

        // shadow with a container
        let shadowOutDiv = document.createElement('div')
        shadowOutDiv.id = 'shadowContainer'
        shadowRoot.appendChild(shadowOutDiv)


        /*If the number of watermarking columns is set to 0, or if the number of watermarking columns is too large to exceed the maximum width of the page, the number of watermarking columns and the interval between the x-axis of the watermarking are recalculated.*/
        if (defaultSettings.watermark_cols == 0 || (parseInt(defaultSettings.watermark_x + defaultSettings.watermark_width * defaultSettings.watermark_cols + defaultSettings.watermark_x_space * (defaultSettings.watermark_cols - 1)) > page_width)) {
            defaultSettings.watermark_cols = parseInt((page_width - defaultSettings.watermark_x + page_offsetLeft) / (defaultSettings.watermark_width + defaultSettings.watermark_x_space));
            defaultSettings.watermark_x_space = parseInt((page_width - defaultSettings.watermark_x + page_offsetLeft - defaultSettings.watermark_width * defaultSettings.watermark_cols) / (defaultSettings.watermark_cols - 1));
        }
        /*If the number of watermarking rows is set to 0, or if the number of watermarking rows is too large to exceed the maximum length of the page, the number of watermarking rows and the y-axis interval of the watermarking are recalculated.*/
        if (defaultSettings.watermark_rows == 0 || (parseInt(defaultSettings.watermark_y + defaultSettings.watermark_height * defaultSettings.watermark_rows + defaultSettings.watermark_y_space * (defaultSettings.watermark_rows - 1)) > page_height)) {
            defaultSettings.watermark_rows = parseInt((page_height - defaultSettings.watermark_y + page_offsetTop) / (defaultSettings.watermark_height + defaultSettings.watermark_y_space));
            defaultSettings.watermark_y_space = parseInt(((page_height - defaultSettings.watermark_y + page_offsetTop) - defaultSettings.watermark_height * defaultSettings.watermark_rows) / (defaultSettings.watermark_rows - 1));
        }

        let x;
        let y;
        for (let i = 0; i < defaultSettings.watermark_rows; i++) {
            y = defaultSettings.watermark_y + (defaultSettings.watermark_y_space + defaultSettings.watermark_height) * i;
            for (let j = 0; j < defaultSettings.watermark_cols; j++) {
                x = defaultSettings.watermark_x + (defaultSettings.watermark_width + defaultSettings.watermark_x_space) * j;

                let mask_div = document.createElement('div');
                let oText = document.createTextNode(defaultSettings.watermark_txt);
                mask_div.appendChild(oText);
                /*Setting Watermark Related Attribute start*/
                mask_div.id = defaultSettings.watermark_prefix + i + j;
                /*Setting Watermark Divid Tilt Display*/
                mask_div.style.webkitTransform = "rotate(-" + defaultSettings.watermark_angle + "deg)";
                mask_div.style.MozTransform = "rotate(-" + defaultSettings.watermark_angle + "deg)";
                mask_div.style.msTransform = "rotate(-" + defaultSettings.watermark_angle + "deg)";
                mask_div.style.OTransform = "rotate(-" + defaultSettings.watermark_angle + "deg)";
                mask_div.style.transform = "rotate(-" + defaultSettings.watermark_angle + "deg)";
                mask_div.style.visibility = "";
                mask_div.style.position = "absolute";
                /*Unchosen*/
                mask_div.style.left = x + 'px';
                mask_div.style.top = y + 'px';
                mask_div.style.overflow = "hidden";
                mask_div.style.zIndex = "9999999";
                mask_div.style.opacity = defaultSettings.watermark_alpha;
                mask_div.style.fontSize = defaultSettings.watermark_fontsize;
                mask_div.style.fontFamily = defaultSettings.watermark_font;
                mask_div.style.color = defaultSettings.watermark_color;
                mask_div.style.textAlign = "center";
                mask_div.style.width = defaultSettings.watermark_width + 'px';
                mask_div.style.height = defaultSettings.watermark_height + 'px';
                mask_div.style.display = "block";
                mask_div.style['-ms-user-select'] = "none";
                /*Setting Watermark Related Attribute end*/
                shadowOutDiv.appendChild(mask_div);

            }
        }
    };

    /*Initialize watermarking, add load and resize events*/
    watermark.init = function (settings) {
        window.addEventListener('load', function () {
            loadMark(settings);
        });
        window.addEventListener('resize', function () {
            loadMark(settings);
        });
        window.addEventListener('DOMContentLoaded', function () {
            loadMark(settings);
        });

    };

    /*Manual loading of watermarking*/
    watermark.load = function (settings) {
        loadMark(settings);
        observerDomReloadMark(settings)
    };

    return watermark;
}))

 

2. Optimizing
But after all, watermarking is related to security. If someone opens the developer's mode easily, the content of the watermarking can be changed or even deleted completely, so it can't be done. We need to monitor the changes of DOM and judge that if the user manually modifies the dom of the watermarking through the developer's tool, we will re-render the watermarking, so that the watermarking can not be modified manually.

Idea: By mutation Obsever to monitor the changes of DOM, to determine whether it is the changes of the watermarking part of the dom, and if so, to re-render?

show me the code:

/* Monitor DOM changes to prevent manual deletion */
    let observerDomReloadMark = (settings) => {
        // Select the node that will be observed for mutations
        let observer_node = document.querySelector('#shadowContainer')
        // Options for the observer (which mutations to observe)
        let config = {
            attributes: true,
            childList: true,
            subtree: true
        };
        // Callback function to execute when mutations are observed
        const mutationCallback = (mutationsList) => {
            // for (let mutation of mutationsList) {
            //     let type = mutation.type;
            //     switch (type) {
            //         case "childList":
            //             console.log("A child node has been added or removed.");
            //             break;
            //         case "attributes":
            //             console.log(`The ${mutation.attributeName} attribute was modified.`);
            //             break;
            //         case "subtree":
            //             console.log(`The subtree was modified.`);
            //             break;
            //         default:
            //             break;
            //     }
            // }
            // loadMark(settings)
            console.log(2222)
        };

        // Create an observer instance linked to the callback function
        let observer = new MutationObserver(mutationCallback);

        // Start observing the target node for configured mutations
        observer.observe(observer_node, config);

        // Later, you can stop observing
        // observer.disconnect();
    }

This method seems perfect, but the reality is very cruel. When we look at the effect in the browser again, we find mutation Obsever can't monitor the change of shadow. what? After looking up the data, we find that shadow is independent of the DOM tree, which is equivalent to the isolated dom. In this case, let's give up pretension, or honest and practical normal DOM implementation, how many watermarks will generate multiple watermarks dom, which will necessarily lead to performance problems. Well, let's find another way.

2. canvas Drawing

1. Basic Ideas

A single watermarking is drawn by canvas, and the base64 encoding of the image is generated by toDataURL method, which is placed in background-image: url() of the watermarking container, and the background image will automatically fill the whole container. The idea is much simpler than the first one.

show me the code:

      var canvas = document.createElement('canvas')
      canvas.id = 'canvas'
      canvas.width = defaultSettings.watermark_width // Width of a single watermarking
      canvas.height = defaultSettings.watermark_height // Height of a single watermarking
      var ctx = canvas.getContext('2d')
      ctx.font = 'normal 12px Microsoft Yahei' // Setting Styles
      ctx.fillStyle = 'rgba(112, 113, 114, 0.2)' // Watermark font color
      ctx.translate(canvas.width/2,canvas.height/2)
      ctx.rotate((defaultSettings.watermark_angle * Math.PI) / 180) // Watermarking deflection angle
      ctx.translate(-canvas.width/2,-canvas.height/2)
      ctx.fillText(defaultSettings.watermark_txt, 30, 20)
      var src = canvas.toDataURL('image/png')

      /* Watermarking Containers */
      var watermark_parent_node = null
      if (defaultSettings.watermark_parent_selector) {
          watermark_parent_node = document.querySelector(defaultSettings.watermark_parent_selector)
      } else {
          watermark_parent_node = document.body
      }
     watermark_parent_node.style.pointerEvents = 'none'
      // Determine whether the container has set the backgroundImage property value
      if (!watermark_parent_node.style.backgroundImage) {
          watermark_parent_node.style.backgroundImage = 'URL(' + src + ')'
      } else {
          watermark_parent_node.style.backgroundImage = watermark_parent_node.style.backgroundImage + ', URL(' + src + ')'
      }

2. Optimizing

The idea of optimization is the same as that of the first scheme, but the advantage of this scheme is that it can directly monitor changes.

III. Contrast of the two schemes

programme Advantage shortcoming
shadow DOM  1. Low coupling and isolation of shadow dom from the original DOM tree will not affect the intrinsic functions of the system.

1. shadow DOM cannot be monitored

2. The cost of tampering with watermarking copy and DOM is low.

3. Complex logic of implementation

canvas drawing

1. Clear implementation logic

2. It is difficult for users to tamper with watermarking data to generate images.

3. Watermarks can be monitored if tampered with

 

1. The watermarking image is placed in background-image. If the background-image attribute is set in the class, it will be overwritten.

Posted by wheakory on Thu, 20 Jun 2019 13:41:03 -0700