AlJohri / docx2pdf

MIT License
506 stars 96 forks source link

Converting a docx I edited with Python3.8 to pdf gives me an error #16

Open Jem2104 opened 4 years ago

Jem2104 commented 4 years ago

First of all, I think this module is awesome as there are nearly no other easy options to convert a docx to a pdf using Python. So if this problem could be solved, I'd be very happy.

I created a script that opens a docx, edit some text variables according to a Tkinter Entry input and then saves it as a new docx. I then want to convert it to a pdf so that I don't need to do that manually but that's the part which I can't get to succeed. I'll share with you my code and all the output it gives me:

My code:

from docx import Document
from docx.shared import Pt
from tkinter import *
from tkinter import messagebox
from tkinter import font as tkfont
from docx2pdf import convert
import docx2pdf
import os

print(docx2pdf.__version__)

root = Tk()
root.config(background='#009688')
root.wm_withdraw()
root.update()
root.title('Contractmaker')

naamhuurder = StringVar(root)
geboortedatum = StringVar(root)
adreshuurder = StringVar(root)
pchuurder = StringVar(root)
woonplaatshuurder = StringVar(root)
adresapp = StringVar(root)
typekamer = StringVar(root)
einddatumcontract = StringVar(root)
begindatumcontract = StringVar(root)
beginmaand1 = StringVar(root)
eindmaand1 = StringVar(root)

# Make all entries empty
def clear():
    entryNames = [naamhuurderr, geboortedatumm, adreshuurderr, pchuurderr, woonplaatshuurderr,
                  einddatumcontractt, begindatumcontractt,
                  beginmaand11, eindmaand11]

    for i in entryNames:
        i.delete(0, END)

# The function that changes the variables and saves it as a pdf in the correct folder
def contractupdater():
    global huur
    global appwp
    global pcapp

    # open the document and set the fontsize and style
    doc = Document('./Contract.docx')
    styles = doc.styles['Normal']
    font = styles.font
    font.size = Pt(9)
    font.name = 'Arial'

    # Remove the spaces from the adress line
    oldadres = adresapp.get()
    nospaceadres = oldadres.replace(' ', '').lower()

    # Handle the dropdown menus
    if adresapp.get() == 'Slotlaan 73' or adresapp.get() == 'Slotlaan 77':
        pcapp = '2902AK'
        appwp = 'Capelle aan den IJssel'
    elif adresapp.get() == 'Albert Cuypstraat 22':
        pcapp = '2902GC'
        appwp = 'Capelle aan den IJssel'

    if typekamer.get() == 'Grote kamer':
        huur = '510'
    elif typekamer.get() == 'Kleine kamer':
        huur = '435'
    elif typekamer.get() == 'Grote kamer gedeeld':
        huur = '800'

    # Check whether the date has been filled in correctly
    try:
        einddatum = einddatumcontract.get()
        laatstecijferpluseen = str(int(einddatum[-1]) + 1)
        verlengentot = str(einddatum[:-1])
        verlengentot += laatstecijferpluseen
    except:
        verlengentot = 'error'

    # Replace the variables with the input
    Dictionary = {"naam.vv": naamhuurder.get(), "gb.vv": geboortedatum.get(), 'adres.vv': adreshuurder.get(),
                  'postcode.vv': pchuurder.get(),
                  'woonplaats.vv': woonplaatshuurder.get(), 'appdres.vv': adresapp.get(), 'apppc.vv': pcapp,
                  'appwp.vv': appwp, 'typekamer.vv': typekamer.get(), 'enddate.vv': einddatumcontract.get(),
                  'begindate.vv': begindatumcontract.get(), 'verlengdatum.vv': verlengentot, 'huur.vv': huur,
                  'begineerstemaand.vv': beginmaand1.get(), 'eindeerstemaand.vv': eindmaand1.get()}

    # Check whether all Entry received input
    entriesWithInput = []
    for key, value in Dictionary.items():
        if len(value) > 0:
            entriesWithInput.append(value)

    # Replace the variables of in the docx to the input if it fullfills the if statements
    if len(entriesWithInput) == len(Dictionary):
        if verlengentot != 'error':
            for i in Dictionary:
                for p in doc.paragraphs:
                    if p.text.find(i) >= 0:
                        p.text = p.text.replace(i, Dictionary[i])

            # Save changed document at the correct place
            doc.save('/Users/Jem/Documents/Huurovereenkomsten/Specifiek/{}/contract{}.docx'.format(nospaceadres,
                                                                                             naamhuurder.get()))

            path = '/Users/Jem/Documents/Huurovereenkomsten/Specifiek/{}/contract{}.docx'.format(nospaceadres, naamhuurder.get())
            os.chmod(path, 0o777)

            convert('/Users/Jem/Documents/Huurovereenkomsten/Specifiek/{}/contract{}.docx'.format(nospaceadres,
                                                                                                       naamhuurder.get()))
            # Show user that the file has succesfully been made
            messagebox.showinfo('Succesvol opgeslagen', 'Het contract is gemaakt en opgeslagen in de folder.')

        else:
            messagebox.showerror('Vul alleen cijfers in bij de datum', 'Vul een datum als volgt in: dd-mm-jjjj')

    else:
        messagebox.showerror('Er ging iets mis',
                             'Controleer of alle data correct is ingevuld en probeer opnieuw.')

# GUI stuff that takes care of the scrollbar
def on_configure(event):
    canvas.configure(scrollregion=canvas.bbox('all'))

def on_mousewheel(event):
    canvas.yview_scroll(int(event.delta), 'units')

# Create some fonts
bold_font = tkfont.Font(weight='bold')

# Create the actual GUI
canvas = Canvas(root, width=450, height=550)
canvas.config(background='#009688')
canvas.pack(side=RIGHT)

scrollbar = Scrollbar(root, command=canvas.yview)
# scrollbar.pack(side=RIGHT, fill='y')

canvas.configure(yscrollcommand=scrollbar.set)
canvas.bind('<Configure>', on_configure)
canvas.bind_all('<MouseWheel>', on_mousewheel)

frame = Frame(canvas)
frame.config(background='#009688')
canvas.create_window((0,0), window=frame)

labelNaamhuurder = Label(frame, text='Naam huurder', bg='#009688', font=bold_font).grid(row=0, column=0, sticky=W, padx=(30, 0), pady=(15, 0))
naamhuurderr = Entry(frame, textvariable=naamhuurder, relief=FLAT, highlightcolor='#9DCCFD')
naamhuurderr.focus_set()
naamhuurderr.grid(row=0, column=2, pady=(15, 0))

labelGeboortedatum = Label(frame, text='Geboortedatum', bg='#009688', font=bold_font).grid(row=1, column=0, pady=(15, 0), sticky=W, padx=(30, 0))
geboortedatumm = Entry(frame, textvariable=geboortedatum, relief=FLAT, highlightcolor='#9DCCFD')
geboortedatumm.grid(row=1, column=2, pady=(15, 0))

labelAdreshuurder = Label(frame, text='Adres huurder', bg='#009688', font=bold_font).grid(row=2, column=0, pady=(15, 0), sticky=W, padx=(30, 0))
adreshuurderr = Entry(frame, textvariable=adreshuurder, relief=FLAT, highlightcolor='#9DCCFD')
adreshuurderr.grid(row=2, column=2, pady=(15, 0))

labelPchuurder = Label(frame, text='Postcode huurder', bg='#009688', font=bold_font).grid(row=3, column=0, pady=(15, 0), sticky=W, padx=(30, 0))
pchuurderr= Entry(frame, textvariable=pchuurder, relief=FLAT, highlightcolor='#9DCCFD')
pchuurderr.grid(row=3, column=2, pady=(15, 0))

labelWoonplaatshuurder = Label(frame, text='Woonplaats huurder', bg='#009688', font=bold_font).grid(row=4, column=0, pady=(15, 0), sticky=W, padx=(30, 0))
woonplaatshuurderr = Entry(frame, textvariable=woonplaatshuurder, relief=FLAT, highlightcolor='#9DCCFD')
woonplaatshuurderr.grid(row=4, column=2, pady=(15, 0))

labelAdresapp = Label(frame, text='Adres appartement', bg='#009688', font=bold_font).grid(row=5, column=0, pady=(15, 0), sticky=W, padx=(30, 0))
appartementen = {'Slotlaan 73', 'Slotlaan 77', 'Albert Cuypstraat 22'}
adresapp.set('Slotlaan 73') # Default option
dropdownMenuhuur = OptionMenu(frame, adresapp, *appartementen)
dropdownMenuhuur.config(width=18)
dropdownMenuhuur.grid(row=5, column=2, pady=(15, 0))

labelTypekamer = Label(frame, text='Type kamer', bg='#009688', font=bold_font).grid(row=6, column=0, pady=(15, 0), sticky=W, padx=(30, 0))
typeKamers = {'Grote kamer', 'Kleine kamer', 'Grote kamer gedeeld'}
typekamer.set('Grote kamer') # Default option
dropdownMenutypekamer = OptionMenu(frame, typekamer, *typeKamers)
dropdownMenutypekamer.config(width=18)
dropdownMenutypekamer.grid(row=6, column=2, pady=(15, 0))

labelEinddatumcontract = Label(frame, text='Eind datum contract', bg='#009688', font=bold_font).grid(row=7, column=0, pady=(15, 0), sticky=W, padx=(30, 0))
einddatumcontractt = Entry(frame, textvariable=einddatumcontract, relief=FLAT, highlightcolor='#9DCCFD')
einddatumcontractt.grid(row=7, column=2, pady=(15, 0))

labelBegindatumcontract = Label(frame, text='Begin datum contract', bg='#009688', font=bold_font).grid(row=8, column=0, pady=(15, 0), sticky=W, padx=(30, 0))
begindatumcontractt = Entry(frame, textvariable=begindatumcontract, relief=FLAT, highlightcolor='#9DCCFD')
begindatumcontractt.grid(row=8, column=2, pady=(15, 0))

labelBeginmaand1 = Label(frame, text='Begin van de eerste maand', bg='#009688', font=bold_font).grid(row=9, column=0, pady=(15, 0), sticky=W, padx=(30, 0))
beginmaand11 = Entry(frame, textvariable=beginmaand1, relief=FLAT, highlightcolor='#9DCCFD')
beginmaand11.grid(row=9, column=2, pady=(15, 0))

labelEindmaand1 = Label(frame, text='Eind van de eerste maand', bg='#009688', font=bold_font).grid(row=10, column=0, pady=(15, 0), sticky=W, padx=(30, 0))
eindmaand11 = Entry(frame, textvariable=eindmaand1, relief=FLAT, highlightcolor='#9DCCFD')
eindmaand11.grid(row=10, column=2, pady=(15, 0))

empty = Button(frame, text='Opnieuw', command=clear, font=bold_font)
empty.config(width=10, fg='#009688', borderwidth=0, relief=RAISED)
empty.configure(highlightbackground='#009688')
empty.grid(row=11, column=0, pady=(25, 0), padx=(80, 0))

converter = Button(frame, text='OK', command=contractupdater, font=bold_font)
converter.config(width=10, fg='#009688', borderwidth=2, relief=RAISED)
converter.configure(highlightbackground='#009688')
converter.grid(row=11, column=2, pady=(25, 0), padx=(0, 80))

root.after(1, root.deiconify)
root.mainloop()

Before I added the os.chmod(path, 0o777), it would ask me for permission. After I added that, it wouldn't ask me for permission anymore but it still didn't work. In this SO question, I've also asked my question. After running the above code, it gives me 0%| | 0/1 [00:02<?, ?it/s] It then opens the file in MS Word but doesn't do anything in Word and then it gives me: {'input': '/Users/Jem/Documents/Huurovereenkomsten/Specifiek/slotlaan73/contractabc.docx', 'output': '/Users/Jem/Documents/Huurovereenkomsten/Specifiek/slotlaan73/contractabc.pdf', 'result': 'error', 'error': 'Error: Er heeft zich een fout voorgedaan.'} 'Er heeft zich een fout voorgedaan.' is Dutch for: an error has occurred.

I'm using python 3.8 and docx2pdf 0.1.7

Really hoping you can find a solution for this because, as said, I think this module can be very useful.

AlJohri commented 4 years ago

hi @Jem2104, if this is something you're still struggling with, can you verify that docx2pdf is the issue by trying to convert this single file using the docx2pdf command line interface?

Jem2104 commented 4 years ago

So you mean by just saving the new updated docx on my laptop and then converting it to pdf through the command line in my IDE?

AlJohri commented 4 years ago

Yup!

Jem2104 commented 4 years ago

I just tried it and it gave me the exact same problem / error as I explained in my initial post. If I need to check anything else, let me know and I'll try it! :)

Really hoping this is something that can be fixed as this module would make my project perfect

AlJohri commented 4 years ago

@Jem2104 yes, if possible can you attach a version of your document that reproduces the error? of course stripped of any sensitive or personal information

Jem2104 commented 4 years ago

Do you mean the code I'm working with? If so, please have a look at the original post of this thread. If you need it in any other format, please let me know and I'll provide it.

If you mean the original docx that get's updated into a new docx and then saved as a new docx before being converted to pdf: this is the one Contract.GithubVersion.docx

sourtin commented 3 years ago

Unfortunately I'm not experienced enough with JXA to find out what's going wrong, but I had the same problem. I solved it by making this AppleScript version -- save it as an applescript droplet app and then just drag the files/folders to convert onto it. You may need to 'prime' word's permissions so that it can access the folders by first dragging the parent folder onto Word, otherwise you could get a 'grant permission' dialog for each file.

jaymegordo commented 3 years ago

@sourtin I'm getting "grant folder access" errors too, what do you mean by "dragging the parent folder onto Word" to 'prime' the permissions? That seems like what I want to do but not sure exactly how...

hannah-earley commented 3 years ago

@sourtin I'm getting "grant folder access" errors too, what do you mean by "dragging the parent folder onto Word" to 'prime' the permissions? That seems like what I want to do but not sure exactly how...

It's been a while, but I think what I did was:

  1. Let's say you've got "World.docx" in a folder "Hello".
  2. Make sure Microsoft Word is launched.
  3. Drag the folder "Hello" onto the Microsoft Word icon in the dock. Nothing much should happen, but the permissions should now be 'primed'.
  4. You should now be able to use the AppleScript Droplet: drag files and or folders onto the Droplet App (make sure all the files/folders are inside a primed folder, such as "Hello" from earlier).
jaymegordo commented 3 years ago

@wearley thanks, I was actually trying to get docx2pdf to work, but that didn't work for me. However what DID work was using this applescript I could run with automator as a quick action:

use scripting additions

property word_docs : {"org.openxmlformats.wordprocessingml.document", "com.microsoft.word.doc"}
property default_path : (path to desktop) as alias
property Delim : {".docx", ".doc"}
property PDF : ".pdf"

on run {input, parameters}
    set outPDF to {}

    -- replace Word document extensions with PDF, and append to outPDF list
    set {TID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, Delim}

    repeat with afile in input
        copy item 1 of text items of (afile as text) & PDF to the end of outPDF
    end repeat

    set AppleScript's text item delimiters to TID

    tell application id "com.microsoft.Word"
        set visible to false
        activate
        repeat with i from 1 to count of input
            open (item i of input)
            set theOutputPath to (item 1 of outPDF)

            open for access file theOutputPath

            tell active document
                save as it file name theOutputPath file format format PDF
                close saving yes
            end tell
        end repeat
        quit
    end tell
    return input
end run

which ALSO didn't work until I specifically added the line open for access file theOutputPath. Link here: https://apple.stackexchange.com/questions/269539/applescript-pages-export-to-pdf-fails-due-to-sandbox-permissions

I'm not super sure how all the mac sandbox permissions work, but that fixed it, and the script works without getting the "grant access for this file..." dialog in word.

@AlJohri Is there any way to add anything similar to docx2pdf to make it work without the permissions dialog?