2016-12-22 61 views
10

Próbuję wygenerować obrazy (jpg lub png) z HTML i już wypróbowałem PhantomJS (przez jonnyw/php-phantomjs w php) i wkhtmltoimage, ale oba mają ten sam problem podczas generowania obrazu. Każdy promień obramowania, obrazy lub czcionki mają naprawdę źle postrzępione krawędzie i nie są ostre.Rozproszone rogi i słabe renderowanie podczas używania Phantomjs (lub wkhtmltoimage) screengrab na Dockerze

Początkowo myślałem, że to nie są załadowane czcionki, ale moje ikony-czcionki działają dobrze, są po prostu kiepskiej jakości. Mam 100 zestawów jakości i uzyskuję takie same wyniki, gdy używam Phantomjs lub wkhtmltoimage na dowolnej stronie internetowej.

enter image description here

Czy ktoś wie co może być tego przyczyną?

UPDATE

enter image description here

UPDATE 2

Oto kod używany z jonnyw/php-phantomjs:

 $client = Client::getInstance(); 
     $client->isLazy(); 
     $client->getEngine()->setPath('phantomjs'); 
     $client->getEngine()->debug(true); 

     $width = 560; 
     $height = 670; 
     $top = 1; 
     $left = 1; 

     $request = $client->getMessageFactory()->createCaptureRequest('https://myurltoscreengrab.com', 'GET'); 
     $request->setOutputFile('uploads/stats/test.png'); 
     $request->setFormat('png'); 

     $request->setViewportSize($width, $height); 
     $request->setCaptureDimensions($width, $height, $top, $left); 

     $response = $client->getMessageFactory()->createResponse(); 

     // Send the request 
     $client->send($request, $response); 

JS używany

/** 
* Set up page and script parameters 
*/ 
var page  = require('webpage').create(), 
    system  = require('system'), 
    response = {}, 
    debug  = [], 
    logs  = [], 
    procedure = {}, 
    resources = 0, 
    timeout; 

/** 
* Global variables 
*/ 


/** 
* Define width & height of capture 
*/ 


var rectTop = 1, 
    rectLeft = 1, 
    rectWidth = 530, 
    rectHeight = 670; 

if(rectWidth && rectHeight) { 

    debug.push(new Date().toISOString().slice(0, -5) + ' [INFO] PhantomJS - Set capture clipping size ~ top: ' + rectTop + ' left: ' + rectLeft + ' ' + rectWidth + 'x' + rectHeight); 

    page.clipRect = { 
     top: rectTop, 
     left: rectLeft, 
     width: rectWidth, 
     height: rectHeight 
    }; 
} 


/** 
* Define paper size. 
*/ 


/** 
* Define viewport size. 
*/ 

var viewportWidth = 530, 
    viewportHeight = 670; 

if(viewportWidth && viewportHeight) { 

    debug.push(new Date().toISOString().slice(0, -5) + ' [INFO] PhantomJS - Set viewport size ~ width: ' + viewportWidth + ' height: ' + viewportHeight); 

    page.viewportSize = { 
     width: viewportWidth, 
     height: viewportHeight 
    }; 
} 




/** 
* Define custom headers. 
*/ 

page.customHeaders = {}; 



/** 
* Page settings 
*/ 

page.settings.resourceTimeout = 5000; 



/** 
* On resource timeout 
*/ 
page.onResourceTimeout = function (error) { 

response  = error; 
response.status = error.errorCode; 


}; 

/** 
* On resource requested 
*/ 
page.onResourceRequested = function (req) { 





    resources++; 
    window.clearTimeout(timeout); 
}; 

/** 
* On resource received 
*/ 
page.onResourceReceived = function (res) { 

    var resource = res; // To be removed in version 5.0 


if(!response.status) { 
    response = resource; 
} 



    if(!res.stage || res.stage === 'end') { 

     resources--; 

     if (resources === 0) { 

      timeout = window.setTimeout(function() { 
       procedure.execute('success'); 
      }, 300); 
     } 
    } 
}; 

/** 
* Handle page errors 
*/ 
page.onError = function (msg, trace) { 

var error = { 
    message: msg, 
    trace: [] 
}; 

trace.forEach(function(t) { 
    error.trace.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function + ')' : '')); 
}); 

logs.push(error); 


}; 

/** 
* Handle global errors 
*/ 
phantom.onError = function(msg, trace) { 

var stack = []; 

trace.forEach(function(t) { 
    stack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function + ')' : '')); 
}); 

response.status = 500; 
response.content = msg; 
response.console = stack; 

system.stdout.write(JSON.stringify(response, undefined, 4)); 
phantom.exit(1); 


}; 

/** 
* Open page 
*/ 
page.open ('https://boxstat.co/widgets/image/stats/2898784/18/500/FFFFFF-EEEEEE-fafafa-333333-85bd4d-ffffff-e4f8cf-71b42f-fddfc1-bd6610-fad3c9-c85639-fac9c9-c52e2e', 'GET', '', function (status) { 


page.evaluate(function() { 

    var styles = {}; 

    for(var property in styles) { 
     document.body.style[property] = styles[property]; 
    } 
}); 

    window.setTimeout(function() { 
     procedure.execute(status); 
    }, 4800); 
}); 

/** 
* Execute procedure 
*/ 
procedure.execute = function (status) { 

if (status === 'success') { 

    try { 

     page.render('uploads/stats/test.png', { 
      format: 'png', 
      quality: 100, 
     }); 

     response.content = page.evaluate(function() { 
      return document.getElementsByTagName('html')[0].innerHTML 
     }); 

    } catch(e) { 

     response.status = 500; 
     response.content = e.message; 
    } 
} 

response.console = logs; 

system.stderr.write(debug.join('\\n') + '\\n'); 
system.stdout.write(JSON.stringify(response, undefined, 4)); 

phantom.exit(); 

}; 
+0

Wygląda na to, że jakość jest niższa niż 100.Czy jesteś całkowicie pewien, że jakość jest ustawiona na 100? Dodaj obraz i kwadrat z jednolitym kolorem, dzięki czemu możesz zobaczyć, czy jest to problem z renderowaniem czcionek. –

+0

Taka jakość wynosi 100, gdy renderuję to jako JPEG. Na domyślnych ustawieniach nawet solidne kolory wyglądają okropnie. Dodałem nowy screengrab do powyższego opisu z jakością 75. – john

+1

Proszę, dodaj swój kod PhantomJS! –

Odpowiedz

0

Dodaj viewportSize i zoomFactor w swoim phantomjs jak:

await page.property('viewportSize', { height: 1600, width: 3600 }); 
await page.property('zoomFactor', 4); 

i/lub dodać:

<script> 
    window.devicePixelRatio = 4; 
</script> 

Spróbuj ustawić współczynnik powiększenia za pomocą wyższą DPI na papierze w stosunku do ekranu DPI:

page.zoomFactor = 300/96; // or use/72 

Musi być ustawione po zdefiniowaniu rozmiaru strony.

Znalazłem również 2 funkcje, które próbują radzić sobie z tego rodzaju problem ...

funkcyjne 1

var makeHighResScreenshot = function(srcEl, destIMG, dpi) { 
    var scaleFactor = Math.floor(dpi/96); 
    // Save original size of element 
    var originalWidth = srcEl.offsetWidth; 
    var originalHeight = srcEl.offsetHeight; 
    // Save original document size 
    var originalBodyWidth = document.body.offsetWidth; 
    var originalBodyHeight = document.body.offsetHeight; 

    // Add style: transform: scale() to srcEl 
    srcEl.style.transform = "scale(" + scaleFactor + ", " + scaleFactor + ")"; 
    srcEl.style.transformOrigin = "left top"; 

    // create wrapper for srcEl to add hardcoded height/width 
    var srcElWrapper = document.createElement('div'); 
    srcElWrapper.id = srcEl.id + '-wrapper'; 
    srcElWrapper.style.height = originalHeight*scaleFactor + 'px'; 
    srcElWrapper.style.width = originalWidth*scaleFactor + 'px'; 
    // insert wrapper before srcEl in the DOM tree 
    srcEl.parentNode.insertBefore(srcElWrapper, srcEl); 
    // move srcEl into wrapper 
    srcElWrapper.appendChild(srcEl); 

    // Temporarily remove height/width constraints as necessary 
    document.body.style.width = originalBodyWidth*scaleFactor +"px"; 
    document.body.style.height = originalBodyHeight*scaleFactor +"px"; 

    window.scrollTo(0, 0); // html2canvas breaks when we're not at the top of the doc, see html2canvas#820 
    html2canvas(srcElWrapper, { 
     onrendered: function(canvas) { 
      destIMG.src = canvas.toDataURL("image/png"); 
      srcElWrapper.style.display = "none"; 
      // Reset height/width constraints 
      document.body.style.width = originalBodyWidth + "px"; 
      document.body.style.height = originalBodyHeight + "px"; 
     } 
    }); 
}; 

Wykorzystanie

var src = document.getElementById("screenshot-source"); 
var img = document.getElementById("screenshot-img"); 
makeHighResScreenshot(src, img, 192); // DPI of 192 is 4x resolution (2x normal DPI for both width and height) 

Funkcja 2

function takeHighResScreenshot(srcEl, destIMG, scaleFactor) { 
    // Save original size of element 
    var originalWidth = srcEl.offsetWidth; 
    var originalHeight = srcEl.offsetHeight; 
    // Force px size (no %, EMs, etc) 
    srcEl.style.width = originalWidth + "px"; 
    srcEl.style.height = originalHeight + "px"; 

    // Position the element at the top left of the document because of bugs in html2canvas. The bug exists when supplying a custom canvas, and offsets the rendering on the custom canvas based on the offset of the source element on the page; thus the source element MUST be at 0, 0. 
    // See html2canvas issues #790, #820, #893, #922 
    srcEl.style.position = "absolute"; 
    srcEl.style.top = "0"; 
    srcEl.style.left = "0"; 

    // Create scaled canvas 
    var scaledCanvas = document.createElement("canvas"); 
    scaledCanvas.width = originalWidth * scaleFactor; 
    scaledCanvas.height = originalHeight * scaleFactor; 
    scaledCanvas.style.width = originalWidth + "px"; 
    scaledCanvas.style.height = originalHeight + "px"; 
    var scaledContext = scaledCanvas.getContext("2d"); 
    scaledContext.scale(scaleFactor, scaleFactor); 

    html2canvas(srcEl, { canvas: scaledCanvas }) 
    .then(function(canvas) { 
     destIMG.src = canvas.toDataURL("image/png"); 
     srcEl.style.display = "none"; 
    }); 
}; 

Wykorzystanie

var src = document.getElementById("screenshot-src"); 
var img = document.getElementById("screenshot-img"); 
takeHighResScreenshot(src, img, 2); // This time we provide desired scale factor directly, no more messing with DPI 

Mam nadzieję, że to pomaga. Teraz pozwól mi powiedzieć, co bym zrobił. Od pewnego czasu robię skrypty automatyzacji przeglądarek, a PhantomJS, IMO, nie jest tak dobry. Rozważ użycie NightmareJS. Jest DUŻO szybszy od Phantoma i łatwiejszy w użyciu.