Capture a Screenshot and Save HTML Page with Map Content

6083
8
Jump to solution
08-03-2015 05:33 PM
JamieWhite1
New Contributor II

Here is my situation, I have developed a web tool utilizing HTML, Javascript, and CSS. The tool looks wonderful on the screen, but when one goes to print the mapDiv does not show and the header/footer and other elements are squished together and makes it unusable to the end user. I have looked around the web a lot today and have been unable to find a solution that works to create a screenshot tool for the end-user to press and get their screenshot. Items I have looked into include:

html2canvas - Does not print the mapDiv

ESRI print digit - Only prints the  mapDiv and nothing from the HTML

phantomjs - Is an executable program which I do not want to run in case of failure

Fireshot API - Works, but only in certain broswers

CSS for printing - many colors not supported and would require a major overhaul to allow usage on many screens

So if anyone has an idea on how to make a button on my page which will in essence print screen the page and then prompt the user to the save the image for their usage later I would be eternally grateful. Also if any of the options I have already explored may be a solution please feel free to share on how to make a tool work in this fashion as well.

Thanks in advance

0 Kudos
1 Solution

Accepted Solutions
SteveCole
Frequent Contributor

Having not seen your application, I don't know if this will work for you but here's what I have done for one of my applications to add a "print map" functionality. I decided to create a print template built on HTML elements and then add the JPEG result of the PrintTask once it completes. Here's a screen shot of the simple map template of HTML elements:

printMapTemplate.jpg

And then once the PrintTask is completed, the resulting JPEG is inserted into a particular DIV element on the page for the final map:

printMapComplete.jpg

Granted, you'll spend some quality time up front coming up with the HTML template but the rest of the code isn't much. You'll notice some extra code that selectively turns on/off legend elements based on what the user currently has visible when they clicked my print map button:

var html;
define ([
  "dojo/_base/declare",
  "esri/tasks/PrintTemplate",
  "esri/tasks/PrintParameters",
  "esri/tasks/PrintTask"
], function (
  declare, PrintTemplate, PrintParameters, PrintTask
) {
    return declare(null,{
  returnPrintMapTemplate: function() {
  html ='your HTML template goes here';
  return html;
  },
  createPrintedMap: function(userTitle) {
  //The new browser window must be opened in the function called by the 'onClick' event so
  //the new window is created here and its associated variable is created with a global scope.
  //Calculate the center of the screen for placement of the new window
  var center_left = (screen.width / 2) - (1100 / 2);
  var center_top = (screen.height / 2) - (600 / 2);
  newWindow = window.open('mapPrint.html','mywindow','width=1100,height=600,menubar=yes,scrollbars=yes,left=' + center_left + ',top=' + center_top);


  //Create the string based version of the HTML page template
  var bodyContent = this.returnPrintMapTemplate();
    
  //Open the new window and obtain a reference to it's DOM
  theDocument = newWindow.document;

  //Swap out the empty DOM of the browser for a string based version of the HTML template
  theDocument.open("text/html","replace");
  theDocument.write(bodyContent);
  theDocument.close();

  //Now begin editing/adding page elements as needed
  if (userTitle.length > 0) {
  theDocument.getElementById("prjName").innerHTML = userTitle;
  } else {
  theDocument.getElementById("prjName").innerHTML = "Project Vicinity";
  }

  censusOn = false;

  if (document.getElementById("raceTractBtn").checked) {
  theDocument.getElementById("censusThemeName").innerHTML = "Minority Population Data:";
  theDocument.getElementById("prjLimits").innerHTML = "Minority Population by Census Tract";
  censusOn = true;
  }


  if (document.getElementById("raceBlockBtn").checked) {
  theDocument.getElementById("censusThemeName").innerHTML = "Minority Population Data:";
  theDocument.getElementById("prjLimits").innerHTML = "Minority Population by Block Group";
  censusOn = true;
  }


  if (document.getElementById("incomeTractBtn").checked) {
  theDocument.getElementById("censusThemeName").innerHTML = "Low Income Data:";
  theDocument.getElementById("prjLimits").innerHTML = "Low Income Population by Census Tract";
  censusOn = true;
  }


  if (document.getElementById("incomeBlockBtn").checked) {
  theDocument.getElementById("censusThemeName").innerHTML = "Low Income Data:";
  theDocument.getElementById("prjLimits").innerHTML = "Low Income Population by Block Group";
  censusOn = true;
  }


  if (document.getElementById("lepTractBtn").checked) {
  theDocument.getElementById("censusThemeName").innerHTML = "Limited English Proficiency Data:";
  theDocument.getElementById("prjLimits").innerHTML = "Limited English Proficiency by Census Tract";
  censusOn = true;
  }


  if (document.getElementById("lepBlockBtn").checked) {
  theDocument.getElementById("censusThemeName").innerHTML = "Limited English Proficiency Data:";
  theDocument.getElementById("prjLimits").innerHTML = "Limited English Proficiency by Block Group";
  censusOn = true;
  }

  if (!censusOn) {
  theDocument.getElementById("censusDataLegend").style.display = "none";
  theDocument.getElementById("prjLimits").innerHTML = "Project Vicinity";


  if (document.getElementById("tractBtn").checked) {
  theDocument.getElementById("prjLimits").innerHTML = "Census Tract Boundaries";
  }

  if (document.getElementById("bgBtn").checked) {
  theDocument.getElementById("prjLimits").innerHTML = "Block Group Boundaries";
  }
  } else {
  theDocument.getElementById("censusDataLegend").style.display = "inline";
  }

  if ((!document.getElementById("tractBtn").checked) || (!document.getElementById("bgBtn").checked)) {
  theDocument.getElementById("censusBoundaryLegend").style.display = "none";
  }

  pTemplate = new PrintTemplate();
  pTemplate.exportOptions = {
  width: 638,
  height: 609,
  dpi: 96
  };
  pTemplate.format = "jpg";
  pTemplate.layout = "MAP_ONLY";
  pTemplate.preserveScale = false;
  pTemplate.showAttribution = false;


  var params = new PrintParameters();
  params.map = app.map;
  params.template = pTemplate;


  jpgMapDiv = theDocument.getElementById("mapAreaBlock");

  pPrintTask = new PrintTask(url);
  pPrintTask.execute(params, function(result) {
  jpgMapDiv.innerHTML = "<img src=\"" + result.url + "\" \>";
  newWindow.alert('Your map is ready!\n\nIn order to print this map properly, please use the following recommended settings under Print Setup:\n\n\u2022 Landscape orientation\n\u2022 0.25" margins\n\u2022 "Print Background Colors and Images" is checked');
  });
  },
  getMapTitle: function() {
  $("#freeow").empty();
  theModalDlg = document.getElementById("mapTitleDialog");
  theContent = theModalDlg.innerHTML;
  theDialogTitle = 'Please provide a map title <span style="font-style:italic">(30 characters Max):</span>';
  $("#freeow").freeow(theDialogTitle, theContent, {
  classes: ["smokey"],
  autoHide: false,
  autoHideDelay: 5000
  }).show();
  },
  adjustCharLeftLabel: function() {
  theTitleTextbox = document.getElementById("txtMapTitle");
  curTitle = theTitleTextbox.value;
  charsLeft = 30 - curTitle.length;
  if (charsLeft < 10) {
  document.getElementById("lblCharsLeft").style.color = "red";
  } else {
  document.getElementById("lblCharsLeft").style.color = "white";
  }
  document.getElementById("lblCharsLeft").innerHTML = charsLeft + " characters remaining";
  },
  screenMapTitle: function() {
  theTitleTextbox = document.getElementById("txtMapTitle");
  curTitle = theTitleTextbox.value;


  if (curTitle.length > 30) {
  $("#freeow").freeow("Whoops!", 'Your map title is more than 30 characters in length. Please revise and try again.', {
  classes: ["simple"],
  autoHide: true,
  autoHideDelay: 5000
  }).show();
  } else {
  $("#freeow").empty();
  app.pf.createPrintedMap(curTitle);
  }
  }
    });
});

Steve

View solution in original post

8 Replies
RickeyFight
MVP Regular Contributor

Jamie,

This is WAB but should be a start.

Screenshot widget 1.0

0 Kudos
JamieWhite1
New Contributor II

Rickey, I do appreciate the starting point but after reviewing it it appears the same thing is happening with the sample that happened when I attempted to invoke html2canvas as well where the map content does not get pushed out to the exported image.

Great starting point, but I think there has to be something else that may work.

0 Kudos
SteveCole
Frequent Contributor

Having not seen your application, I don't know if this will work for you but here's what I have done for one of my applications to add a "print map" functionality. I decided to create a print template built on HTML elements and then add the JPEG result of the PrintTask once it completes. Here's a screen shot of the simple map template of HTML elements:

printMapTemplate.jpg

And then once the PrintTask is completed, the resulting JPEG is inserted into a particular DIV element on the page for the final map:

printMapComplete.jpg

Granted, you'll spend some quality time up front coming up with the HTML template but the rest of the code isn't much. You'll notice some extra code that selectively turns on/off legend elements based on what the user currently has visible when they clicked my print map button:

var html;
define ([
  "dojo/_base/declare",
  "esri/tasks/PrintTemplate",
  "esri/tasks/PrintParameters",
  "esri/tasks/PrintTask"
], function (
  declare, PrintTemplate, PrintParameters, PrintTask
) {
    return declare(null,{
  returnPrintMapTemplate: function() {
  html ='your HTML template goes here';
  return html;
  },
  createPrintedMap: function(userTitle) {
  //The new browser window must be opened in the function called by the 'onClick' event so
  //the new window is created here and its associated variable is created with a global scope.
  //Calculate the center of the screen for placement of the new window
  var center_left = (screen.width / 2) - (1100 / 2);
  var center_top = (screen.height / 2) - (600 / 2);
  newWindow = window.open('mapPrint.html','mywindow','width=1100,height=600,menubar=yes,scrollbars=yes,left=' + center_left + ',top=' + center_top);


  //Create the string based version of the HTML page template
  var bodyContent = this.returnPrintMapTemplate();
    
  //Open the new window and obtain a reference to it's DOM
  theDocument = newWindow.document;

  //Swap out the empty DOM of the browser for a string based version of the HTML template
  theDocument.open("text/html","replace");
  theDocument.write(bodyContent);
  theDocument.close();

  //Now begin editing/adding page elements as needed
  if (userTitle.length > 0) {
  theDocument.getElementById("prjName").innerHTML = userTitle;
  } else {
  theDocument.getElementById("prjName").innerHTML = "Project Vicinity";
  }

  censusOn = false;

  if (document.getElementById("raceTractBtn").checked) {
  theDocument.getElementById("censusThemeName").innerHTML = "Minority Population Data:";
  theDocument.getElementById("prjLimits").innerHTML = "Minority Population by Census Tract";
  censusOn = true;
  }


  if (document.getElementById("raceBlockBtn").checked) {
  theDocument.getElementById("censusThemeName").innerHTML = "Minority Population Data:";
  theDocument.getElementById("prjLimits").innerHTML = "Minority Population by Block Group";
  censusOn = true;
  }


  if (document.getElementById("incomeTractBtn").checked) {
  theDocument.getElementById("censusThemeName").innerHTML = "Low Income Data:";
  theDocument.getElementById("prjLimits").innerHTML = "Low Income Population by Census Tract";
  censusOn = true;
  }


  if (document.getElementById("incomeBlockBtn").checked) {
  theDocument.getElementById("censusThemeName").innerHTML = "Low Income Data:";
  theDocument.getElementById("prjLimits").innerHTML = "Low Income Population by Block Group";
  censusOn = true;
  }


  if (document.getElementById("lepTractBtn").checked) {
  theDocument.getElementById("censusThemeName").innerHTML = "Limited English Proficiency Data:";
  theDocument.getElementById("prjLimits").innerHTML = "Limited English Proficiency by Census Tract";
  censusOn = true;
  }


  if (document.getElementById("lepBlockBtn").checked) {
  theDocument.getElementById("censusThemeName").innerHTML = "Limited English Proficiency Data:";
  theDocument.getElementById("prjLimits").innerHTML = "Limited English Proficiency by Block Group";
  censusOn = true;
  }

  if (!censusOn) {
  theDocument.getElementById("censusDataLegend").style.display = "none";
  theDocument.getElementById("prjLimits").innerHTML = "Project Vicinity";


  if (document.getElementById("tractBtn").checked) {
  theDocument.getElementById("prjLimits").innerHTML = "Census Tract Boundaries";
  }

  if (document.getElementById("bgBtn").checked) {
  theDocument.getElementById("prjLimits").innerHTML = "Block Group Boundaries";
  }
  } else {
  theDocument.getElementById("censusDataLegend").style.display = "inline";
  }

  if ((!document.getElementById("tractBtn").checked) || (!document.getElementById("bgBtn").checked)) {
  theDocument.getElementById("censusBoundaryLegend").style.display = "none";
  }

  pTemplate = new PrintTemplate();
  pTemplate.exportOptions = {
  width: 638,
  height: 609,
  dpi: 96
  };
  pTemplate.format = "jpg";
  pTemplate.layout = "MAP_ONLY";
  pTemplate.preserveScale = false;
  pTemplate.showAttribution = false;


  var params = new PrintParameters();
  params.map = app.map;
  params.template = pTemplate;


  jpgMapDiv = theDocument.getElementById("mapAreaBlock");

  pPrintTask = new PrintTask(url);
  pPrintTask.execute(params, function(result) {
  jpgMapDiv.innerHTML = "<img src=\"" + result.url + "\" \>";
  newWindow.alert('Your map is ready!\n\nIn order to print this map properly, please use the following recommended settings under Print Setup:\n\n\u2022 Landscape orientation\n\u2022 0.25" margins\n\u2022 "Print Background Colors and Images" is checked');
  });
  },
  getMapTitle: function() {
  $("#freeow").empty();
  theModalDlg = document.getElementById("mapTitleDialog");
  theContent = theModalDlg.innerHTML;
  theDialogTitle = 'Please provide a map title <span style="font-style:italic">(30 characters Max):</span>';
  $("#freeow").freeow(theDialogTitle, theContent, {
  classes: ["smokey"],
  autoHide: false,
  autoHideDelay: 5000
  }).show();
  },
  adjustCharLeftLabel: function() {
  theTitleTextbox = document.getElementById("txtMapTitle");
  curTitle = theTitleTextbox.value;
  charsLeft = 30 - curTitle.length;
  if (charsLeft < 10) {
  document.getElementById("lblCharsLeft").style.color = "red";
  } else {
  document.getElementById("lblCharsLeft").style.color = "white";
  }
  document.getElementById("lblCharsLeft").innerHTML = charsLeft + " characters remaining";
  },
  screenMapTitle: function() {
  theTitleTextbox = document.getElementById("txtMapTitle");
  curTitle = theTitleTextbox.value;


  if (curTitle.length > 30) {
  $("#freeow").freeow("Whoops!", 'Your map title is more than 30 characters in length. Please revise and try again.', {
  classes: ["simple"],
  autoHide: true,
  autoHideDelay: 5000
  }).show();
  } else {
  $("#freeow").empty();
  app.pf.createPrintedMap(curTitle);
  }
  }
    });
});

Steve

View solution in original post

JamieWhite1
New Contributor II

Steve,

That may be the result I go with in the end. I like the thought process behind it as well. I have a few other items to knock off my list before I undertake an endeavor to add in more code for printing but thanks for sharing it for sure. If you or anyone else can think of a way to do a screenshot I am definitely all ears.

Here is a screen capture of my page

Here is the jsfiddle (Please bare with me as a I am a bit new to some of this):

Edit fiddle - JSFiddle

SteveCole
Frequent Contributor

In the interest of full disclosure, my approach is a simplified version of what you can do behind the scenes with ArcGIS Server where you have the ability to create/edit map templates that your end user can select from when they click a print map button. I haven't done this because, within my organization, I don't really have access to the location where these templates are stored. Anyways here's a link and link from the Help Docs about this. This gets a little confusing for me but between the Help doc and some forum searches about it, you'll get a better idea about pursuing that option.

0 Kudos
JamieWhite1
New Contributor II

I really do appreciate the idea, believe me I do. The main reason I want the screenshot is due to the selection of items on the screen and the search result box being populated. The map solution you have provided will work, but the coding required to push the information of the search results to the template is what I am uncertain of.

0 Kudos
SteveCole
Frequent Contributor

This is a bit simplistic but your Search Results box is an HTML element so you can pull out the contents if you know the name of the HTML element. Assuming the ID is "searchResults":

var theResults = document.getElementById("searchResults").innerText; //or innerHTML (whichever works best)

Now populate it in your print template using the same technique:

theDocument.getElementById("searchResultsContent").innerHTML = theResults;

There will be some trial and error to get the content and to format it the way you want it.

0 Kudos
JamieWhite1
New Contributor II

I like your thought process. I will test out some of your ideas and if they work you will be the correct answer until a better tool comes around.

FYI .innerHTML from what I have read is not best practice anymore if you use jQuery

http://www.joezimjs.com/javascript/jquery-html-youre-doing-it-wrong/

Just some food for thought

0 Kudos