Quando si scrive di uscita per /dev/stdout/
o /dev/stderr/
su Windows, PhantomJS
passa attraverso le seguenti fasi (come si vede nel render
metodo in \phantomjs\src\webpage.cpp):
- in assenza di
/dev/stdout/
e /dev/stderr/
una tem il percorso del file porary è allocato.
- Chiama il
renderPdf
con il percorso del file temporaneo.
- Renderizza la pagina Web su questo percorso file.
- Leggere il contenuto di questo file in un
QByteArray
.
- Chiamare
QString::fromAscii
sull'array di byte e scrivere su stdout
o stderr
.
- Elimina il file temporaneo.
Per cominciare, ho creato la sorgente per PhantomJS
, ma ho commentato la cancellazione del file. Alla prossima esecuzione, sono stato in grado di esaminare il file temporaneo che aveva reso, che si è rivelato essere completamente soddisfacente. Ho anche provato a eseguire phantomjs.exe rasterize.js http://google.com > test.png
con gli stessi risultati. Questo ha immediatamente escluso un problema di rendering, o qualcosa di specifico da fare con i PDF, il che significa che il problema doveva essere correlato al modo in cui i dati vengono scritti su stdout
.
A questo punto avevo dei sospetti sull'esistenza di alcuni imbrogli di codifica del testo. Dalle esecuzioni precedenti, avevo sia una versione valida che non valida dello stesso file (un PNG in questo caso).
Utilizzando alcuni codice C#, ho eseguito il seguente esperimento:
//Read the contents of the known good file.
byte[] bytesFromGoodFile = File.ReadAllBytes("valid_file.png");
//Read the contents of the known bad file.
byte[] bytesFromBadFile = File.ReadAllBytes("invalid_file.png");
//Take the bytes from the valid file and convert to a string
//using the Latin-1 encoding.
string iso88591String = Encoding.GetEncoding("iso-8859-1").GetString(bytesFromGoodFile);
//Take the Latin-1 encoded string and retrieve its bytes using the UTF-8 encoding.
byte[] bytesFromIso88591String = Encoding.UTF8.GetBytes(iso88591String);
//If the bytes from the Latin-1 string are all the same as the ones from the
//known bad file, we have an encoding problem.
Debug.Assert(bytesFromBadFile
.Select((b, i) => b == bytesFromIso88591String[i])
.All(c => c));
Nota che ho usato la codifica ISO-8859-1 come QT
utilizza questo come il default encoding for c-strings. Come si è scoperto, tutti quei byte erano uguali. Il punto di quell'esercizio era vedere se potevo imitare le fasi di codifica che hanno reso invalidi dati validi.
Per ulteriori prove, ho studiato \phantomjs\src\system.cpp e \phantomjs\src\filesystem.cpp.
- Nel
system.cpp
, la classe System
stive riferimenti, tra le altre cose, File
oggetti per stdout
, stdin
e stderr
, che sono impostati per utilizzare la codifica UTF-8
.
- Durante la scrittura su
stdout
, viene richiamata la funzione write
dell'oggetto File
. Questa funzione supporta la scrittura su file di testo e binari, ma a causa del modo in cui la classe System
li inizializza, tutta la scrittura verrà trattata come se si trattasse di un file di testo.
Così il problema si riduce a questo: abbiamo bisogno di essere l'esecuzione di una scrittura binaria a stdout
, eppure le nostre scritture finiscono per essere trattati come testo e avere una codifica applicato a loro che fa sì che il file risultante non valida.
Dato il problema descritto in precedenza, non riesco a vedere alcun modo per ottenere questo lavoro nel modo desiderato su Windows senza apportare modifiche al codice PhantomJS
. Quindi sono qui:
Questo primo cambiamento fornirà una funzione che possiamo chiamare il File
oggetti per eseguire in modo esplicito una scrittura binaria.
Aggiungere funzione il seguente prototipo in \phantomjs\src\filesystem.h
:
bool binaryWrite(const QString &data);
e luogo la sua definizione nel \phantomjs\src\filesystem.cpp
(il codice per questo metodo deriva dal metodo write
in questo file):
bool File::binaryWrite(const QString &data)
{
if (!m_file->isWritable()) {
qDebug() << "File::write - " << "Couldn't write:" << m_file->fileName();
return true;
}
QByteArray bytes(data.size(), Qt::Uninitialized);
for(int i = 0; i < data.size(); ++i) {
bytes[i] = data.at(i).toAscii();
}
return m_file->write(bytes);
}
A circa la linea 920 di \phantomjs\src\webpage.cpp
vedrete un blocco di codice che assomiglia a questo:
if(fileName == STDOUT_FILENAME){
#ifdef Q_OS_WIN32
_setmode(_fileno(stdout), O_BINARY);
#endif
((File *)system->_stderr())->write(QString::fromAscii(name.constData(), name.size()));
#ifdef Q_OS_WIN32
_setmode(_fileno(stdout), O_TEXT);
#endif
}
Change a questo:
if(fileName == STDOUT_FILENAME){
#ifdef Q_OS_WIN32
_setmode(_fileno(stdout), O_BINARY);
((File *)system->_stdout())->binaryWrite(QString::fromAscii(ba.constData(), ba.size()));
#elif
((File *)system->_stderr())->write(QString::fromAscii(name.constData(), name.size()));
#endif
#ifdef Q_OS_WIN32
_setmode(_fileno(stdout), O_TEXT);
#endif
}
Allora, cosa che la sostituzione del codice non fa altro che richiama la nostra nuova funzione binaryWrite
, ma lo fa sorvegliato da un blocco #ifdef Q_OS_WIN32
. L'ho fatto in questo modo in modo da preservare la vecchia funzionalità su sistemi non Windows che non sembrano presentare questo problema (o no?). Si noti che questa correzione è valida solo per la scrittura stdout
- se si vuole si può sempre applicarlo a stderr
ma potrebbe non importa poi così tanto in quel caso.
Nel caso in cui si desideri solo un file binario precompilato (chi non lo sarebbe?), È possibile trovare phantomjs.exe
con queste correzioni sul mio SkyDrive. La mia versione è di circa 19 MB mentre quella che ho scaricato in precedenza era solo di circa 6 MB, anche se ho seguito le istruzioni here, quindi dovrebbe andare bene.
quale versione di phantomjs? prova ad aggiornare alla versione più recente. – philfreo
Sto vedendo questo stesso problema su 1.9.2 Win8x64. Non piping l'output sembra avere qualche contenuto pdf nella console, ma piping l'output direttamente sul file tramite phantomjs rasterize.js> test.pdf non ha nulla da fare. –
@philfreo Ho usato 1.9.2 su Win7 – michaeltintiuc