[Python]wkhtmltopdf als Python subprocess levert lege pdf op

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • EXX
  • Registratie: Juni 2001
  • Laatst online: 12:08

EXX

EXtended eXchange

Topicstarter
Ik probeer vanuit een Python programma een html file om te zetten naar een pdf file. Dit levert echter een lege pdf op. hier het stukje code:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
        cmd_line = "%s %s %s %s" % (Path(constants.RESOURCE_DIR + "wkhtmltox/bin/wkhtmltopdf.exe").resolve(),"--enable-local-file-access",
                              Path(constants.REPORTS_DIR + invoice_name + ".html").resolve(), 
                              Path(constants.REPORTS_DIR + invoice_name + ".pdf").resolve())
                   
        cmd_str = ""           
        for i in range(len(cmd_line)):
            cmd_str += '/' if cmd_line[i] == '\\' else cmd_line[i]

        cmd = cmd_str.split()
        print ("command string: %s" % cmd_str)
        print ("subprocess parameters:\n%s\n%s\n%s\n%s" % (cmd[0], cmd[1], cmd[2], cmd[3]))
        
        result = subprocess.call([cmd[0], cmd[1], cmd[2], cmd[3]])

        print (result)


De output in het CMD venster van waaruit ik het Python programma start:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
N:\qc>python admin_prog.py
command string: //NAS/home/qc/Resources/wkhtmltox/bin/wkhtmltopdf.exe --enable-local-file-access //NAS/home/qc/Reports/Z-0002.html //NAS/home/qc/Reports/Z-0002.pdf
subprocess parameters:
//NAS/home/qc/Resources/wkhtmltox/bin/wkhtmltopdf.exe
--enable-local-file-access
//NAS/home/qc/Reports/Z-0002.html
//NAS/home/qc/Reports/Z-0002.pdf
Loading pages (1/6)
Counting pages (2/6)
Resolving links (4/6)
Loading headers and footers (5/6)
Printing pages (6/6)
Done
0


De terminal output laat zien dat het html naar pdf omzetten plaatsvindt en dat dit zonder fouten loopt, aangezien de return code 0 is. Het pdf bestand is echter leeg.

Als ik echter de commando string direct uitvoer:
code:
1
2
3
4
5
6
7
N:\qc> //NAS/home/qc/Resources/wkhtmltox/bin/wkhtmltopdf.exe --enable-local-file-access //NAS/home/qc/Reports/Z-0002.html //NAS/home/qc/Reports/Z-0002.pdf
Loading pages (1/6)
Counting pages (2/6)
Resolving links (4/6)
Loading headers and footers (5/6)
Printing pages (6/6)
Done

dan wordt er wel een goed pdf bestand gemaakt, met daarin de inhoud van het html bestand.

Ipv een subprocess.call heb ik ook subprocess.run geprobeerd.
Verder heb ik ook geprobeerd:

code:
1
        os.system(cmd_str)


Ook dit levert een lege pdf op.

De laatste poging was via een batch bestand, maar ook dan een lege pdf.

Wat gaat hier fout?

Relevante software en hardware die ik gebruik:
Windows 10
Python 3.8.10
wkhtmltopdf 0.12.6 (with patched qt)

For it is the doom of men that they forget...           Huidige en vroegere hardware specs         The Z80 is still alive!

Beste antwoord (via RobIII op 23-12-2024 19:22)


  • EXX
  • Registratie: Juni 2001
  • Laatst online: 12:08

EXX

EXtended eXchange

Topicstarter
Even een samenvatting die als antwoord kan dienen:

Het probleem lag niet bij wkhtmltopdf en ook niet bij het Python subprocess. De betreft een gewone programmeerfout:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    with open(constants.REPORTS_DIR + invoice_name + ".html", "w") as invoice_file:
    
        invoice_file.write("<!DOCTYPE html>\n<html>\n<head>\n")
    
        # .......
        # fill html file with content
        # .......
         
        invoice_file.write("</body>\n</html>\n")

     

        cmd_line = r"%s %s %s %s" % (Path(constants.RESOURCE_DIR + "wkhtmltox/bin/wkhtmltopdf.exe").resolve(),"--enable-local-file-access",
                               Path(constants.REPORTS_DIR + invoice_name + ".html").resolve(), 
                               Path(constants.REPORTS_DIR + invoice_name + ".pdf").resolve())
                   
        cmd_str = ""           
        for i in range(len(cmd_line)):
            cmd_str += '/' if cmd_line[i] == '\\' else cmd_line[i]

        cmd = cmd_str.split()

        result = subprocess.run([cmd[0], cmd[1], cmd[2], cmd[3]], shell=True)


Het stukje subprocess code valt binnen het "with..." blok waardoor de html file nog open staat en het door wkhtmltopdf niet kan worden gelezen. Resultaat: een op zich geldige pdf file, echter zonder inhoud. Door het stukje supprocess code buiten het "w'ith..." blok te plaatsen is het proleem opgelost.

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    with open(constants.REPORTS_DIR + invoice_name + ".html", "w") as invoice_file:
    
        invoice_file.write("<!DOCTYPE html>\n<html>\n<head>\n")
    
        # .......
        # fill html file with content
        # .......
         
        invoice_file.write("</body>\n</html>\n")

     

    cmd_line = r"%s %s %s %s" % (Path(constants.RESOURCE_DIR + "wkhtmltox/bin/wkhtmltopdf.exe").resolve(),"--enable-local-file-access",
                               Path(constants.REPORTS_DIR + invoice_name + ".html").resolve(), 
                               Path(constants.REPORTS_DIR + invoice_name + ".pdf").resolve())
                   
    cmd_str = ""           
    for i in range(len(cmd_line)):
        cmd_str += '/' if cmd_line[i] == '\\' else cmd_line[i]

    cmd = cmd_str.split()

    result = subprocess.run([cmd[0], cmd[1], cmd[2], cmd[3]], shell=True)


Helaas kan ik een eigen posting niet als antwoord markeren.

For it is the doom of men that they forget...           Huidige en vroegere hardware specs         The Z80 is still alive!

Alle reacties


Acties:
  • 0 Henk 'm!

  • jeroen3
  • Registratie: Mei 2010
  • Laatst online: 22:13
Kan de beperkte webkit in Qt de pagina wel renderen?

Acties:
  • +1 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
Ik heb goede ervaringen met headless chromium om html te renderen naar pdf, misschien n optie?

Acties:
  • 0 Henk 'm!

  • EXX
  • Registratie: Juni 2001
  • Laatst online: 12:08

EXX

EXtended eXchange

Topicstarter
jeroen3 schreef op zaterdag 21 december 2024 @ 21:11:
Kan de beperkte webkit in Qt de pagina wel renderen?
Het programma kan de pagina renderen. Wanneer ik de conversie vanaf de cmd prompt uitvoer krijg ik een prima pdf. Als ik exact hetzelfde doe dmv een subprocess vanuit python krijg ik echter een lege pdf.

For it is the doom of men that they forget...           Huidige en vroegere hardware specs         The Z80 is still alive!


Acties:
  • 0 Henk 'm!

  • -MD-
  • Registratie: Mei 2004
  • Laatst online: 30-08 22:16
Geen verstand van Python maar een wild guess: kan het een rechten iets zijn i.c.m. je NAS? Probeer het omzetten eens lokaal met een lokaal bestand? Want in de ene casus draait het script lokaal en in het andere geval op je NAS. Die flag is leuk, maar als het lokaal wel lukt is er wellicht toch een gebrek aan privilege ondanks de flag.

Edit: oh, en wellicht vanuit je NAS gezien zijn je paden niet correct? Kun je via een terminal op die wijze de betreffende html openen?

[ Voor 20% gewijzigd door -MD- op 21-12-2024 23:38 ]


Acties:
  • 0 Henk 'm!

  • EXX
  • Registratie: Juni 2001
  • Laatst online: 12:08

EXX

EXtended eXchange

Topicstarter
Rechtstreeks vanaf de command prompt werkt alles prima.

For it is the doom of men that they forget...           Huidige en vroegere hardware specs         The Z80 is still alive!


Acties:
  • 0 Henk 'm!

  • sh4d0wman
  • Registratie: April 2002
  • Laatst online: 18:15

sh4d0wman

Attack | Exploit | Pwn

Oh, dit heb ik ook al eens gehad maar weet niet zeker meer of ik de code nog ergens heb. :X
(Ik voel de frustratie nog haha.)

Ik zal vanavond even kijken voor je indien het nog niet verholpen is. Volgens mij had ik het antwoord uit een stackoverflow topic...

This signature has been taken down by the Dutch police in the course of an international lawenforcement operation.


Acties:
  • 0 Henk 'm!

  • Tk55
  • Registratie: April 2009
  • Niet online
Je code checkt niet of het resultaat van het subprocess succesvol is. Je moet de exit code checken (check=True). Ook “capture” je de output niet, dus weet je niet wat de eventuele error is.

Gebruik daarvoor subprocess.run: https://docs.python.org/3...ocess.html#subprocess.run
If capture_output is true, stdout and stderr will be captured. When used, the internal Popen object is automatically created with stdout and stderr both set to PIPE. The stdout and stderr arguments may not be supplied at the same time as capture_output. If you wish to capture and combine both streams into one, set stdout to PIPE and stderr to STDOUT, instead of using capture_output.
Daarnaast een paar tips:
- gebruik f-strings: f”{var1}/{var2}”
- Pathlib: Als je je eerste pad een Path maakt, dan kun je daarna alles chainen: Path / “mydir” /myfile.txt”

[ Voor 40% gewijzigd door Tk55 op 22-12-2024 10:18 ]


Acties:
  • 0 Henk 'm!

  • MsG
  • Registratie: November 2007
  • Laatst online: 22:23

MsG

Forumzwerver

Wat als je een normale schijfletterkoppeling maakt voor de netwerkshare, werkt het dan ook niet? Dat het commando los wel werkt zegt niet altijd alles.

Als je het Python script bijvoorbeeld gewoon eens lokaal zet op C:\test en de pdf-tool ook in dezelfde map. Zo kan je iig uitsluiten of er wellicht met die paden iets raars is.

Denk om uw spatiegebruik. Dit scheelt Tweakers.net kostbare databaseruimte! | Groninger en geïnteresseerd in Domotica? Kom naar DomoticaGrunn


Acties:
  • +1 Henk 'm!

  • bananasplit
  • Registratie: December 2024
  • Laatst online: 15-01 15:39
Als je op deze manier een commandline opbouwt dat je aan het subprocess doorgeeft moet je ook Shell=True opgeven anders kan het ontvangende programma (wkhtmltopdf.exe in dit geval) die commandline niet op de juiste manier processen.

Beter is het om de argumenten in een list te zetten in plaats van een string en het run commando te gebruiken in plaats van het verouderde subprocess.call command.

Zoiets bijvoorbeeld:
Python:
1
2
3
4
5
6
7
8
from subprocess import run
Cmd = ['//NAS/home/qc/Resources/wkhtmltox/bin/wkhtmltopdf.exe']
Cmd+= ['--enable-local-file-access']
Cmd+= ['//NAS/home/qc/Reports/Z-0002.html']
Cmd+= ['//NAS/home/qc/Reports/Z-0002.pdf']

result = run(Cmd)
print(result)


PS :
Aangezien je pathlib gebruikt is die loop om slashes om te zetten in forward slashes overbodig

[ Voor 6% gewijzigd door bananasplit op 22-12-2024 11:17 ]


Acties:
  • 0 Henk 'm!

  • EXX
  • Registratie: Juni 2001
  • Laatst online: 12:08

EXX

EXtended eXchange

Topicstarter
MsG schreef op zondag 22 december 2024 @ 10:22:
Wat als je een normale schijfletterkoppeling maakt voor de netwerkshare, werkt het dan ook niet? Dat het commando los wel werkt zegt niet altijd alles.

Als je het Python script bijvoorbeeld gewoon eens lokaal zet op C:\test en de pdf-tool ook in dezelfde map. Zo kan je iig uitsluiten of er wellicht met die paden iets raars is.
Even op de C drive getest: hetzelfde (foute) resultaat.

Wat me wel opvalt is dat de gegeneerde pdf file wel 1,235 bytes groot is. Er wordt dus wel een bestand gecreeerd en er staat ook iets in. Een pdf viewer maakt de pdf dan ook zonder morren open. De pdf is echter blanco, dus geen inhoud.

De html bron is op zich goed te renderen, want anders zou het vanaf de command prompt ook niet werken. Het lijkt erop dat de html file gewoon niet goed ingelezen wordt.
bananasplit schreef op zondag 22 december 2024 @ 10:23:
Als je op deze manier een commandline opbouwt dat je aan het subprocess doorgeeft moet je ook Shell=True opgeven anders kan het ontvangende programma (wkhtmltopdf.exe in dit geval) die commandline niet op de juiste manier processen.

Beter is het om de argumenten in een list te zetten in plaats van een string en het run commando te gebruiken in plaats van het verouderde subprocess.call command.

Zoiets bijvoorbeeld:
Python:
1
2
3
4
5
6
7
8
from subprocess import run
Cmd = ['//NAS/home/qc/Resources/wkhtmltox/bin/wkhtmltopdf.exe']
Cmd+= ['--enable-local-file-access']
Cmd+= ['//NAS/home/qc/Reports/Z-0002.html']
Cmd+= ['//NAS/home/qc/Reports/Z-0002.pdf']

result = run(Cmd)
print(result)


PS :
Aangezien je pathlib gebruikt is die loop om slashes om te zetten in forward slashes overbodig
Een list geruiken doe ik al: de command line wordt gesplits met cmd_line.split() en de list wordt aan het subprocess.call command gevoed.

Ik dacht ook dat omzetten van backslash naar forward slash niet nodig zou zijn, maar de praktijk laat anders zien. Als ik de backslashes niet omzet krijg ik allerlei foutmeldingen.

Edit:
Ik heb eens even om te testen de volgende regel gebruikt:

code:
1
result = subprocess.run(["type", "C:\\qc\\Reports\\Z-0002.html"], shell=True)

Dit levert helemaal geen output op in de terminal

Dit daarintegen wel:
code:
1
result = subprocess.run(["dir", "C:\\qc\\"], shell=True)

Ik krijg mooi de directory listing te zien in de terminal.

Echter, als ik naar de directory listing kijk, zie ik dit:
code:
1
2
3
4
5
6
7
8
9
10
11
12
 Volume in drive C is SSD
 Volume Serial Number is 426E-EB0D

 Directory of C:\qc\Reports

2024-12-22  16:37    <DIR>          .
2024-12-22  16:37    <DIR>          ..
2024-12-20  18:08             2,415 QC-0001.html
2024-12-16  15:13            76,064 report.html
2024-12-22  17:10                 0 Z-0002.html
               3 File(s)         78,479 bytes
               2 Dir(s)  250,439,258,112 bytes free


Kijk naar de filesize van de bron (Z-0002.html): aangegeven wordt nul bytes. Dit is echter onzin. Het bestand bevat html code en is 2,593 bytes groot (dit wordt aangegeven door de windows explorer).

Aaargggh, ik vermoed dat het html bestand nog open staat als ik het subprocess start...

Edit:
En ja hoor, het html bestand stond inderdaad open. |:(

Het stukje code voor het subprocess stond binnen de "with..." sectie waarmee het html bestand wordt geopend om het met inhoud te vullen.

De subprocess code 1 ident niveau naar links en opgelost...

[ Voor 24% gewijzigd door EXX op 22-12-2024 18:24 ]

For it is the doom of men that they forget...           Huidige en vroegere hardware specs         The Z80 is still alive!


Acties:
  • Beste antwoord
  • 0 Henk 'm!

  • EXX
  • Registratie: Juni 2001
  • Laatst online: 12:08

EXX

EXtended eXchange

Topicstarter
Even een samenvatting die als antwoord kan dienen:

Het probleem lag niet bij wkhtmltopdf en ook niet bij het Python subprocess. De betreft een gewone programmeerfout:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    with open(constants.REPORTS_DIR + invoice_name + ".html", "w") as invoice_file:
    
        invoice_file.write("<!DOCTYPE html>\n<html>\n<head>\n")
    
        # .......
        # fill html file with content
        # .......
         
        invoice_file.write("</body>\n</html>\n")

     

        cmd_line = r"%s %s %s %s" % (Path(constants.RESOURCE_DIR + "wkhtmltox/bin/wkhtmltopdf.exe").resolve(),"--enable-local-file-access",
                               Path(constants.REPORTS_DIR + invoice_name + ".html").resolve(), 
                               Path(constants.REPORTS_DIR + invoice_name + ".pdf").resolve())
                   
        cmd_str = ""           
        for i in range(len(cmd_line)):
            cmd_str += '/' if cmd_line[i] == '\\' else cmd_line[i]

        cmd = cmd_str.split()

        result = subprocess.run([cmd[0], cmd[1], cmd[2], cmd[3]], shell=True)


Het stukje subprocess code valt binnen het "with..." blok waardoor de html file nog open staat en het door wkhtmltopdf niet kan worden gelezen. Resultaat: een op zich geldige pdf file, echter zonder inhoud. Door het stukje supprocess code buiten het "w'ith..." blok te plaatsen is het proleem opgelost.

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    with open(constants.REPORTS_DIR + invoice_name + ".html", "w") as invoice_file:
    
        invoice_file.write("<!DOCTYPE html>\n<html>\n<head>\n")
    
        # .......
        # fill html file with content
        # .......
         
        invoice_file.write("</body>\n</html>\n")

     

    cmd_line = r"%s %s %s %s" % (Path(constants.RESOURCE_DIR + "wkhtmltox/bin/wkhtmltopdf.exe").resolve(),"--enable-local-file-access",
                               Path(constants.REPORTS_DIR + invoice_name + ".html").resolve(), 
                               Path(constants.REPORTS_DIR + invoice_name + ".pdf").resolve())
                   
    cmd_str = ""           
    for i in range(len(cmd_line)):
        cmd_str += '/' if cmd_line[i] == '\\' else cmd_line[i]

    cmd = cmd_str.split()

    result = subprocess.run([cmd[0], cmd[1], cmd[2], cmd[3]], shell=True)


Helaas kan ik een eigen posting niet als antwoord markeren.

For it is the doom of men that they forget...           Huidige en vroegere hardware specs         The Z80 is still alive!


Acties:
  • +1 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
EXX schreef op zondag 22 december 2024 @ 16:46:
[...]
Wat me wel opvalt is dat de gegeneerde pdf file wel 1,235 bytes groot is. Er wordt dus wel een bestand gecreeerd en er staat ook iets in. Een pdf viewer maakt de pdf dan ook zonder morren open. De pdf is echter blanco, dus geen inhoud.
Beetje mosterd na de maaltijd, maar als je de PDF eens in notepad (of beter: een hex editor) gooit, staat er dan niet gewoon een ordinaire foutmelding in ofzo?
EXX schreef op maandag 23 december 2024 @ 19:06:
Helaas kan ik een eigen posting niet als antwoord markeren.
Bij deze :P

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij

Pagina: 1