pyqt / python-qt5

Unofficial PyQt5 via PyPI for Python 2.7 64-bit on Windows
GNU General Public License v3.0
280 stars 77 forks source link

PyQt5 print to PDF has different fonts in file using docker #78

Open godomainz opened 11 months ago

godomainz commented 11 months ago

different_fonts

have a look at above image. File downloaded using my PyQT code has different fonts. I'm using docker ubuntu:20.04 image to run my code. If I run python code without docker font seems correct. Below is my python code(htmlToPdfnew.py)

import sys
from PyQt5 import QtWidgets, QtWebEngineWidgets
from PyQt5.QtCore import QUrl, QTimer, QDateTime, Qt
from PyQt5.QtGui import QPageLayout, QPageSize
from PyQt5.QtWidgets import QApplication
from PyQt5.QtNetwork import QNetworkCookie
import argparse
import os
import json

class Window(QtWebEngineWidgets.QWebEngineView):
    def __init__(self, *args, **kwargs):
        super(Window, self).__init__(*args, **kwargs)
        self.cookieStore = self.page().profile().cookieStore()

    def initCookies(self, cookie_file):
        if cookie_file:
            print("Cookie parameter specified "+cookie_file)
            with open("output/"+cookie_file, encoding='utf8') as f:
                cookies = json.load(f)

            for cookie in cookies:
                qcookie = QNetworkCookie()
                qcookie.setName(cookie.get('name', '').encode())
                qcookie.setValue(cookie.get('value', '').encode())
                qcookie.setDomain(cookie.get('domain', ''))
                qcookie.setPath(cookie.get('path', ''))
                qcookie.setExpirationDate(
                    QDateTime.fromString(str(cookie.get('expirationDate', 0)),
                                         Qt.ISODate))
                qcookie.setHttpOnly(cookie.get('httpOnly', False))
                qcookie.setSecure(cookie.get('secure', False))
                self.cookieStore.setCookie(qcookie, QUrl())

def main():
    file_name = 'ABC123.pdf'
    cookie_file = None
    parser = argparse.ArgumentParser(description="Just an example", formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument("--url", help="Type url", required=True)
    parser.add_argument("--output", help="Type output pdf file name")
    parser.add_argument("--cookie", help="Type cookie file name")
    parser.add_argument("--orientation", help="Type Orientation Portrait or Landscape")
    args = parser.parse_args()
    config = vars(args)
    url = config['url']
    output = config['output']
    cookie = config['cookie']
    orientation = config['orientation']
    if output:
        file_name = output
    if cookie:
        cookie_file = cookie
    print(url);
    app = QtWidgets.QApplication(sys.argv)
    loader = Window()
    if cookie_file:
        loader.initCookies(cookie_file)
    loader.setZoomFactor(1)
    layout = QPageLayout()
    layout.setPageSize(QPageSize(QPageSize.A4))

    if orientation == 'Landscape':
        layout.setOrientation(QPageLayout.Landscape)
    else:
        layout.setOrientation(QPageLayout.Portrait)
    loader.load(QUrl(url))
    loader.page().pdfPrintingFinished.connect(lambda *args: QApplication.exit())

    def emit_pdf():
        directory = "/htmltopdf/output/"
        if not os.path.exists(directory):
            os.makedirs(directory)
        QTimer.singleShot(2000, lambda: loader.page().printToPdf(directory+file_name, pageLayout=layout))

    def generate_pdf():
        # Wait for 5 seconds after the page has finished loading before generating the PDF
        QTimer.singleShot(5000, emit_pdf)

    loader.loadFinished.connect(generate_pdf)
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

Below is my Dockerfile

FROM ubuntu:20.04 as ubuntupyqt
ENV DEBIAN_FRONTEND=noninteractive
RUN adduser --quiet --disabled-password qtuser && usermod -a -G video qtuser
RUN apt-get update -y
RUN apt-get install alsa -y
RUN apt-get install libnss3 -y
RUN apt-get install python3-pip -y
RUN pip3 install --upgrade pip
RUN pip3 install pyqt5
RUN pip3 install --default-timeout=900 pyqtwebengine

FROM ubuntupyqt as stage-ubuntu

ARG ARG_VCS_REF
ARG ARG_VERSION_STICKER

LABEL \
    maintainer="https://github.com/accetto" \
    vendor="accetto" \
    version-sticker="${ARG_VERSION_STICKER}" \
    org.label-schema.vcs-ref="${ARG_VCS_REF}" \
    org.label-schema.vcs-url="https://github.com/accetto/ubuntu-vnc-xfce"

### 'apt-get clean' runs automatically
RUN apt-get update \
    && DEBIAN_FRONTEND=noninteractive apt-get install -y \
        inetutils-ping \
        lsb-release \
        net-tools \
        unzip \
        vim \
        zip \
        curl \
        git \
        wget \
        nano
RUN rm -rf /var/lib/apt/lists/*

### install current 'jq' explicitly
RUN \
    { \
    JQ_VERSION="1.6" ; \
    JQ_DISTRO="jq-linux64" ; \
    cd /tmp ; \
    wget -q "https://github.com/stedolan/jq/releases/download/jq-${JQ_VERSION}/${JQ_DISTRO}" ; \
    wget -q "https://raw.githubusercontent.com/stedolan/jq/master/sig/v${JQ_VERSION}/sha256sum.txt" ; \
    test=$(grep "${JQ_DISTRO}" sha256sum.txt | sha256sum -c | grep -c "${JQ_DISTRO}: OK") ; \
    if [ $test -ne 1 ] ; then \
        echo "FAILURE: ${JQ_DISTRO} failed checksum test" ; \
        exit 1 ; \
    else \
        rm sha256sum.txt ; \
        chown root "${JQ_DISTRO}" ; \
        chmod +x "${JQ_DISTRO}" ; \
        # mv -f "${JQ_DISTRO}" $(which jq) ; \
        mv -f "${JQ_DISTRO}" /usr/bin/jq ; \
    fi ; \
    cd - ; \
    }

### next ENTRYPOINT command supports development and should be overriden or disabled
### it allows running detached containers created from intermediate images, for example:
### docker build --target stage-vnc -t dev/ubuntu-vnc-xfce:stage-vnc .
### docker run -d --name test-stage-vnc dev/ubuntu-vnc-xfce:stage-vnc
### docker exec -it test-stage-vnc bash
# ENTRYPOINT ["tail", "-f", "/dev/null"]

FROM stage-ubuntu as stage-xfce

ENV \
    LANG='en_US.UTF-8' \
    LANGUAGE='en_US:en' \
    LC_ALL='en_US.UTF-8'

### 'apt-get clean' runs automatically
RUN apt-get update \
    && DEBIAN_FRONTEND=noninteractive apt-get install -y \
        mousepad \
        locales \
        supervisor \
        xfce4 \
        xfce4-terminal \
    && locale-gen en_US.UTF-8 \
    && apt-get purge -y \
        pm-utils \
        xscreensaver* \
    && rm -rf /var/lib/apt/lists/*

FROM stage-xfce as stage-vnc

### 'apt-get clean' runs automatically
### installed into '/usr/share/usr/local/share/vnc'
### Bintray has been deprecated and disabled since 2021-05-01
# RUN wget -qO- https://dl.bintray.com/tigervnc/stable/tigervnc-1.10.1.x86_64.tar.gz | tar xz --strip 1 -C /
# RUN wget -qO- https://github.com/accetto/tigervnc/releases/download/v1.10.1-mirror/tigervnc-1.10.1.x86_64.tar.gz | tar xz --strip 1 -C /
RUN wget -qO- https://sourceforge.net/projects/tigervnc/files/stable/1.10.1/tigervnc-1.10.1.x86_64.tar.gz | tar xz --strip 1 -C /

FROM stage-vnc as stage-novnc

### same parent path as VNC
ENV NO_VNC_HOME=/usr/share/usr/local/share/noVNCdim

### 'apt-get clean' runs automatically
### 'python-numpy' used for websockify/novnc
### ## Use the older version of websockify to prevent hanging connections on offline containers,
### see https://github.com/ConSol/docker-headless-vnc-container/issues/50
### installed into '/usr/share/usr/local/share/noVNCdim'
RUN apt-get update \
    && DEBIAN_FRONTEND=noninteractive apt-get install -y \
        python-numpy \
    && mkdir -p ${NO_VNC_HOME}/utils/websockify \
    && wget -qO- https://github.com/novnc/noVNC/archive/v1.2.0.tar.gz | tar xz --strip 1 -C ${NO_VNC_HOME} \
    && wget -qO- https://github.com/novnc/websockify/archive/v0.9.0.tar.gz | tar xz --strip 1 -C ${NO_VNC_HOME}/utils/websockify \
    && chmod +x -v ${NO_VNC_HOME}/utils/*.sh \
    && rm -rf /var/lib/apt/lists/*

### add 'index.html' for choosing noVNC client
RUN echo \
"<!DOCTYPE html>\n" \
"<html>\n" \
"    <head>\n" \
"        <title>noVNC</title>\n" \
"        <meta charset=\"utf-8\"/>\n" \
"    </head>\n" \
"    <body>\n" \
"        <p><a href=\"vnc_lite.html\">noVNC Lite Client</a></p>\n" \
"        <p><a href=\"vnc.html\">noVNC Full Client</a></p>\n" \
"    </body>\n" \
"</html>" \
> ${NO_VNC_HOME}/index.html

FROM stage-novnc as stage-wrapper

### 'apt-get clean' runs automatically
### Install nss-wrapper to be able to execute image as non-root user
RUN apt-get update \
    && DEBIAN_FRONTEND=noninteractive apt-get install -y \
        gettext \
        libnss-wrapper \
    && rm -rf /var/lib/apt/lists/*

FROM stage-wrapper as stage-final

LABEL \
    any.accetto.description="Headless Ubuntu VNC/noVNC container with Xfce desktop" \
    any.accetto.display-name="Headless Ubuntu/Xfce VNC/noVNC container" \
    any.accetto.expose-services="6901:http,5901:xvnc" \
    any.accetto.tags="ubuntu, xfce, vnc, novnc"

### Arguments can be provided during build
ARG ARG_HOME
ARG ARG_REFRESHED_AT
ARG ARG_VERSION_STICKER
ARG ARG_VNC_BLACKLIST_THRESHOLD
ARG ARG_VNC_BLACKLIST_TIMEOUT
ARG ARG_VNC_PW
ARG ARG_VNC_RESOLUTION

ENV \
    DISPLAY=:1 \
    HOME=${ARG_HOME:-/home/headless} \
    NO_VNC_PORT="6901" \
    REFRESHED_AT=${ARG_REFRESHED_AT} \
    STARTUPDIR=/dockerstartup \
    VERSION_STICKER=${ARG_VERSION_STICKER} \
    VNC_BLACKLIST_THRESHOLD=${ARG_VNC_BLACKLIST_THRESHOLD:-20} \
    VNC_BLACKLIST_TIMEOUT=${ARG_VNC_BLACKLIST_TIMEOUT:-0} \
    VNC_COL_DEPTH=24 \
    VNC_PORT="5901" \
    VNC_PW=${ARG_VNC_PW:-headless} \
    VNC_RESOLUTION=${ARG_VNC_RESOLUTION:-1360x768} \
    VNC_VIEW_ONLY=false

### Creates home folder
WORKDIR ${HOME}

COPY [ "./src/startup", "${STARTUPDIR}/" ]

### Preconfigure Xfce
COPY [ "./src/home/Desktop", "./Desktop/" ]
COPY [ "./src/home/config/xfce4/panel", "./.config/xfce4/panel/" ]
COPY [ "./src/home/config/xfce4/xfconf/xfce-perchannel-xml", "./.config/xfce4/xfconf/xfce-perchannel-xml/" ]

### 'generate_container_user' has to be sourced to hold all env vars correctly
RUN echo 'source $STARTUPDIR/generate_container_user' >> ${HOME}/.bashrc

### Fix permissions
RUN chmod +x \
        "${STARTUPDIR}/set_user_permissions.sh" \
        "${STARTUPDIR}/vnc_startup.sh" \
        "${STARTUPDIR}/version_of.sh" \
        "${STARTUPDIR}/version_sticker.sh" \
    && gtk-update-icon-cache -f /usr/share/icons/hicolor \
    && "${STARTUPDIR}"/set_user_permissions.sh "${STARTUPDIR}" "${HOME}"

EXPOSE ${VNC_PORT} ${NO_VNC_PORT}

USER qtuser
ENV XDG_RUNTIME_DIR /home/qtuser
ENV QT_QPA_PLATFORM=offscreen
ENV QT_QPA_PLATFORM_PLUGIN_PATH=/opt/Qt/${QT_VERSION}/gcc_64/plugins
ENV DISPLAY=:1

### Issue #7: Mitigating problems with foreground mode
WORKDIR ${STARTUPDIR}
COPY htmlToPdfnew.py /htmltopdf/htmlToPdfnew.py
COPY htmltopdf.sh /htmltopdf/htmltopdf.sh
ENTRYPOINT ["sh", "/htmltopdf/htmltopdf.sh"]
CMD [ "--wait" ]

This is how I build and run my command

docker build -t htmltopdf .
docker run --rm -it -v /tmp:/htmltopdf/output htmltopdf:latest --url https://example.com  --output testnew.pdf --cookie mycookie.txt

Below is my htmltopdf.sh

#!/bin/bash
./vnc_startup.sh &
cd /htmltopdf/
python3 htmlToPdfnew.py "$@"