hakuna
hakuna

Reputation: 6701

angularjs unsafe:data; when capturing screenshot with html2canvas

I am using angularjs and html2canvas for capturing a screenshot. It captures well some times on few screens and doesn't some times. I get the following error:

enter image description here tried Angular changes urls to "unsafe:" in extension page and added the following in my config.js

   app.config(['$compileProvider', function ($compileProvider) {
    $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|local|data):/);
}]);

It helped to the extent that i am not getting the unsafe:data; error any more on click of my image capture button, but the image is not getting captured as well. Now the html tag looks as below:

<img ng-src="data:," class="img-responsive" src="data:,">

instead of the actual base64 url for my image/png (e.g: <img ng-src="data:image/png;base64,Mybase64Url). I have added ng-csp directive in my index.html and changed every <img src=""> to <img ng-src="">.

camera-form.html:

<form name="captureForm" ng-submit="ctrl.save()">
    <div class="modal-header">
        <h3 class="modal-title">Captured Screen shot</h3>
    </div>
    <div class="modal-body">
        <div class="container-fluid padding">
            <div class="row">
                <div class="col-sm-8">
                    <img ng-src="{{ctrl.imageURL}}" class="img-responsive" id="capturedImage">
                </div>
                <div class="col-sm-4">
                    <div class="form-group">
                        <input placeholder="Title" name="title" required class="form-control" ng-model="ctrl.title">

                        <div ng-show="captureForm.$submitted || captureForm.title.$dirty"
                             ng-messages="captureForm.title.$error">
                            <div ng-message="required">Title is required</div>
                        </div>
                    </div>

                    <div class="form-group">
                        <select name="category" ng-model="ctrl.category" class="form-control" required>
                            <option value="1">Knowledge</option>
                            <option value="2">Irregular</option>
                            <option value="3">Periodic maintenance</option>
                        </select>
                        <div ng-show="captureForm.$submitted || captureForm.category.$dirty"
                             ng-messages="captureForm.category.$error">
                            <div ng-message="required">Category is required</div>
                        </div>
                    </div>

                    <div class="form-group">
                    <textarea placeholder="Comment" name="comment" required class="form-control"
                              ng-model="ctrl.comment"></textarea>

                        <div ng-show="captureForm.$submitted || captureForm.comment.$dirty"
                             ng-messages="captureForm.comment.$error">
                            <div ng-message="required">Comment is required</div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <div class="modal-footer">
        <input type="submit" class="btn btn-info" value="Save">
        <a href ng-click="ctrl.cancel()" class="btn btn-danger">Cancel</a>
    </div>
</form>

camera.directive.js:

(function () {
    'use strict';

    angular
        .module('myModule')
        .directive('camera', camera);

    camera.$inject = ['$rootScope'];

    /* @ngInject */
    function camera($rootScope) {
        var directive = {
            link: link,
            restrict: 'A'
        };

        return directive;

        function link(scope, element, attrs) {
            $rootScope.$on('capture', function (event, deferred) {
                event.stopPropagation();

                renderSvg(element);

                //method to render the SVG's using canvg
                function renderSvg(element) {
                    // First render all SVGs to canvases
                    var elements = element.find('svg').map(function () {
                        var svg = $(this);
                        var canvas = $('<canvas></canvas>');
                        svg.replaceWith(canvas);

                        // Get the raw SVG string and curate it
                        var content = svg.wrap('<p></p>').parent().html();
                        content = content.replace(/xlink:title="hide\/show"/g, "");
                        content = encodeURIComponent(content);
                        svg.unwrap();

                        // Create an image from the svg
                        var image = new Image();
                        image.src = 'data:image/svg+xml,' + content;
                        image.onload = function () {
                            canvas[0].width = image.width;
                            canvas[0].height = image.height;

                            // Render the image to the canvas
                            var context = canvas[0].getContext('2d');
                            context.drawImage(image, 0, 0);
                        };
                        return {
                            svg: svg,
                            canvas: canvas
                        };
                    });

                        // At this point the container has no SVG, it only has HTML and Canvases.
                        html2canvas(element[0], {
                            useCORS: true,
                            allowTaint: true,
                            onrendered: function (canvas) {
                                // Put the SVGs back in place
                                elements.each(function () {
                                    this.canvas.replaceWith(this.svg);
                                });

                                var dataURL = null;

                                try {
                                    dataURL = canvas.toDataURL("image/png", 0.9);
                                    deferred.resolve(dataURL);
                                }
                                catch (e) {
                                    deferred.reject(e);
                                }
                            }
                        });
                }
            });
        }
    }
})();

camera.controller.js:

/**
 * CameraController.js
 */
(function () {
    'use strict';

    angular
        .module('myModule')
        .controller('CameraController', CameraController);

    CameraController.$inject = ['$rootScope', 'cabinetService', 'SweetAlert', 'growl', '$q', '$uibModal', 'spinner'];

    function CameraController($scope, cabinetService, SweetAlert, growl, $q, $uibModal, spinner) {

        var ctrl = this;

        ctrl.captureScreenShot = captureScreenShot;

        function captureScreenShot($event) {
            var deferred = $q.defer();

            $scope.$emit('capture', deferred);

            deferred.promise.then(uploadScreenShot);
        }

        function uploadScreenShot(imageURL) {
            var modalInstance = $uibModal.open({
                animation: true,
                templateUrl: 'app/components/capture/camera-form.html',
                controller: ModalCtrl,
                controllerAs: 'ctrl',
                size: 'lg',
                resolve: {
                    imageURL: function () {
                        return imageURL;
                    }
                }
            });

            modalInstance.result.then(function (captureData) {
                var data = {
                    Base64Url: captureData.image.split(',')[1],
                    Title: captureData.title,
                    FileCategory:parseInt(captureData.category),
                    Comment: captureData.comment,
                    FunctionalityName: $scope.$state.current.name
                };

                spinner.spinnerShow();

                //call the service to upload the screen shot
                cabinetService
                    .uploadScreenShot(data)
                    .then(function (data) {
                        SweetAlert.swal({
                                title: 'Screen shot saved successfully!',
                                text: '',
                                type: 'info',
                                showCancelButton: true,
                                confirmButtonColor: '#DD6B55',
                                confirmButtonText: 'Open cabinet',
                                closeOnConfirm: true
                            },
                            function (isConfirm) {
                                if (isConfirm) {
                                    $scope.$state.go('index.cabinet')
                                }
                            });
                    })
                    .catch(function (err) {
                        SweetAlert.swal("Cannot capture the screen shot", "Something went wrong, please try again !", "error");
                        growl.err('Unable to upload the screen shot', {ttl: 20000});
                    })
                    .finally(spinner.spinnerHide);
            }, function () {
                $log.info('Modal dismissed at: ' + new Date());
            });
        }
    }

    ModalCtrl.$inject = ['$scope', '$uibModalInstance', 'imageURL', 'growl'];

    function ModalCtrl($scope, $uibModalInstance, imageURL, growl) {
        var ctrl = this;

        ctrl.imageURL = imageURL;
        ctrl.save = save;
        ctrl.cancel = cancel;

        function activate() {
        }

        function save() {
            var form = $scope.captureForm;

            if (form.$dirty && form.$valid) {
                $uibModalInstance.close({
                    image: imageURL,
                    title: ctrl.title,
                    comment: ctrl.comment,
                    category: ctrl.category
                });
            } else {
                growl.info('Please fill out all the fields');
            }
        }

        function cancel() {
            $uibModalInstance.dismiss();
        }
    }
}());

Is there anything i need to do with the above regular expression or i am missing something else ? please help !

Upvotes: 1

Views: 2436

Answers (2)

hakuna
hakuna

Reputation: 6701

For some reason , the element that's being passed as $event parameter on click is not passing the right div element every time. Instead passed the exact div element i wanted in the place of element and it worked.

Upvotes: 0

Shankar Gurav
Shankar Gurav

Reputation: 1067

Hi you need to alter image src attribute with jquery as

$("#canvas_image").attr("src", canvasdata);

I have tried with this Plunker

Within this plunker I have added directive as well if you want to utilize it.

Upvotes: 1

Related Questions