I have in interesting CSS/JavaScript problem. I am building a user-friendly web map that will include volunteered geographic information and needs to have the ability to print at multiple paper sizes, up to poster size. For the interface, I am working with Leaflet.js, Mapbox.js, and jQuery. The way I've approached printing is to set up a preview window that shows overlays only (no tileLayer) on a completely new L.Map with a white background, with the markers scaled proportionally to the paper size selected by the user. The idea is that the map will fill the page and the markers will always print at the same size (8 mm across for circle markers, 10 mm for icons). Here's a screenshot of the preview window in Firefox:
There is a fair bit of complex code. Suffice it to say that whenever the user changes the window size or paper orientation, the preview box and icons resize accordingly. Whenever the user changes the paper size, the icons resize but the preview box does not, so as to represent the correct size ratios. Here are the functions I'm using to do it:
function adjustPreviewBox(){
//set preview box dimensions based on print window size and paper orientation
if ($("#paperOrientation option[value=portrait]").prop("selected")){
var height = $("#printBox").height() - 61;
var width = height / Math.sqrt(2);
$("#printPreview").height(height);
$("#printPreview").width(width);
} else {
//first set by horizontal dimension
var width = $("#printBox").width() - 300;
var height = width / Math.sqrt(2);
//check for vertical overflow
if (height > $("#printBox").height() - 61){
height = $("#printBox").height() - 61;
width = height * Math.sqrt(2);
};
$("#printPreview").height(height);
$("#printPreview").width(width);
}
};
function adjustScale(){
//change symbol sizes and ratio scale according to paper size
var prevWidth = $("#printPreview").width();
var prevHeight = $("#printPreview").height();
var size = $("#paperSize select option:selected").val();
var series = size[0];
var pScale = Number(size[1]);
var longside, mmppPaper;
if (series == "A"){ //equations for long side lengths in mm, minus 10mm print margins
longside = Math.floor(1000/(Math.pow(2,(2*pScale-1)/4)) + 0.2) - 20;
} else if (series == "B"){
longside = Math.floor(1000/(Math.pow(2,(pScale-1)/2)) + 0.2) - 20;
};
//find the mm per pixel ratio
mmppPaper = prevWidth > prevHeight ? longside / prevWidth : longside / prevHeight;
var mapZoom = printPreviewMap.getZoom();
var scaleText = $("#printBox .leaflet-control-scale-line").html().split(" ");
var multiplier = scaleText[1] == "km" ? 1000000 : 1000;
var scalemm = Number(scaleText[0]) * multiplier;
var scalepx = Number($("#printBox .leaflet-control-scale-line").width());
var mmppMap = scalemm / scalepx;
var denominator = Math.round(mmppMap / mmppPaper);
$("#ratioScale span").text(denominator);
return [mmppMap, mmppPaper];
}
function resizeMarkers(markerType, init){
//scale preview marker size based on paper size and orientation
markerType == "circle" ? changeRadius(init) : changeIconSize(init);
};
function getRadius(){
//adjust ratio scale and return scale ratios
var scales = adjustScale();
var mmppPaper = scales[1];
return 4 / mmppPaper;
};
function changeRadius(init){
//each circle marker will print at 8 mm diameter regardless of map scale and page size
var radius = getRadius();
printPreviewMap.eachLayer(function(layer){
if (typeof layer._radius !== 'undefined'){
if (init == true){
layer.setStyle({
opacity: 1,
fillOpacity: 1
});
layer.unbindPopup();
};
layer.setRadius(radius);
}
});
};
function changeIconSize(init){
//each icon will print at 10 mm per side regardless of map scale and page size
var side = 2.5 * getRadius();
//need to change dimensions and offset
$("#printPreview .leaflet-marker-icon").css({
width: side + "px",
height: side + "px",
"margin-left": -(side / 2),
"margin-top": -(side / 2)
})
};
I have @media print
CSS styles that seem to work well for printing the preview window:
@media print {
@page {
size: auto;
margin: 10mm;
}
#printBox, #printPreview {
position: absolute;
max-height: 100%;
bottom: 0;
left: 0;
top: 0;
right: 0;
}
#printPreview {
position: absolute !important;
width: 100% !important;
height: 100% !important;
border: none;
}
#scalegrip {
visibility: hidden;
}
#container {
visibility: hidden;
}
}
I've tested this by printing to a PDF using Adobe's driver. Here's the result:
It seems to work fine--except that the markers only fill the upper-left part of the page, whereas I would like them to expand outward to fill the entire page so that the final product is the same 'view' as the preview box. This is where I'm stumped and would welcome any advice or ideas from anyone who has tried something similar or knows their way around printing websites.
In a similar project, I had to force the map to refresh after any CSS size changes through the invalidateSize method. For example using jQuery to change map height and weight div:
$("map").css('width', '267mm');
$("map").css('height', '210mm');
map.invalidateSize();
According to leaflet help:
invalidateSize: Checks if the map container size changed and updates the map if so — call it after you've changed the map size dynamically, also animating pan by default.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With