Word in PowerPoint konvertieren und korrekt für den Druck formatiern

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Caldzera
User
Beiträge: 4
Registriert: Sonntag 19. Mai 2019, 10:51

Hallo zusammen

Ich hoffe, dass ich mit meiner Anfrage hier richtig bin. Ein Kommilitone hat zwei Scripts geschrieben, meiner Vermutung nach in Python (da er sich mit Python auskennt und dies immer mal wieder vertiefte). Da er mir die Scripts nicht überlassen will, diese aber für mich einen enormen Mehrwert haben, wäre ich bereit, in die Programmierwelt einzutauchen dafür :)

Folgende Ausgangslage: für unser Studium schreiben wir die Notizen in Word, wobei Aufzählungsebene 1 jeweils die Frage für eine Karteikarte darstellt und Ebenen 2-4 die entsprechenden Antworten.
Mittels eines Scripts wandelt er diese Word-Dateien dann in ein PowerPoint um, wobei die Ebene 1 als eine Frage auf einer PowerPoint-Folie landet. Die dazugehörigen Antworten 2-4 werden im PowerPoint auf die nachfolgende Folie geschrieben.

Wenn ich mich nicht täusche, speichert er das PowerPoint dann als PDF, wobei zunächst jedes Slide eine Seite ist. (vielleicht macht er dies auch mit dem Script "all in one"). Das Script packt dann 8 Frage-Slides auf eine A4 Seite und die 8 dazugehörigen Antwortslides auf eine weitere A4 Seite - wir müssen die Dokumente dann nur noch im Duplex ausdrucken und ausschneiden, schon haben wir die Karteikarten.

So, diesbezüglich nun meine Frage:
1. Ist Python für sowas überhaupt sinnvoll, oder gäbe es da eine einfachere Lösung?
2. Ich bin ein Neuling was Python und generell Programmierung angeht, ich habe nur mal in einem Kurs mit node.js und angular gearbeitet. Deshalb wäre ich um Inputs froh, wo ich denn am besten anfangen sollte bei diesem Vorhaben :?

Danke euch allen schon mal im Voraus!
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Was für ein doofer Kommilitone.

Wenn ich das Problem hätte, würde ich

- https://python-docx.readthedocs.io/en/latest/ zum einlesen der Word Dokumente nehmen.
- daraus Latex Beamer Slides erzeugen.
- die slides mit pdflatex zu einem PDF machen.

Grund für die letzten beiden Schritte: direkt PDF zu erzeugen ist mühsam und fehleranfällig.
Caldzera
User
Beiträge: 4
Registriert: Sonntag 19. Mai 2019, 10:51

Das klingt schon mal vielversprechend, danke dafür!

Er ist leider ein seltsamer Zeitgenosse, will immer nur profitieren und nie teilen. Er wollte mir nicht einmal sagen, mit welcher Sprache dies geschrieben ist.

Mal angenommen, man lässt den ersten Schritt, also das Konvertieren von Word zu PowerPoint, aus - denn man könnte das Ganze theoretisch ja auch manuell direkt in die Folien schreiben. Wieso wir das ohnehin nicht getan haben weiss ich nicht - er meinte, so wäre es besser. Nun ja; Womit sollte ich mich dann am besten auseinandersetzen? Ich habe mal manuell rumprobiert, am vielversprechendsten war bisher das Speichern der PowerPoint als PDF, wobei ein Slide eine PDF-Seite ist. Das Drucken jedoch ist nicht gerade optimal verlaufen, folgendes Problem:
weder Windows noch MacOS erlauben es mir, 8 Slides auf eine Seite zu drucken (und 6 füllen die Seite nicht genügend aus, da geht einiges an Papier unnötig verloren bei über 3000 Karteikarten). Zudem stimmten die Antworten auf der Rückseite nicht (Positionen müssten getauscht werden)

Ich hätte also gerne: 8 Karten pro Seite und die korrekten Antworten auf der Rückseite. Die Reihenfolge müsste für den Druck so aussehen:
- angenommen, es wären 8 Karten, also 16 PowerPoint Slides in einer Datei. Ungerade Seitenzahlen sind immer die Fragen. Dann müsste die Vorderseite die Seiten 1-3-5-7-9-11-13-15 und auf die Rückseite (wenn man das Blatt an der langen Kante drehen würde) die Reihenfolge wie folgt aussehen: 4-2-8-6-12-10-16-14

Was würde sich für sowas anbieten? :)
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn du nicht konvertieren willst, dann brauchst du auch nicht programmieren. Es ist eine Frage des Druckens, wie man mehrere pdf Seiten auf eine bekommt. Wenn das OS das nicht kann, dann gibts da Command line Tools für.

Zb https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/, aber da findet sich bestimmt auch noch anderes.
Caldzera
User
Beiträge: 4
Registriert: Sonntag 19. Mai 2019, 10:51

Das Konvertieren werde ich mir auf jeden Fall mal anschauen, aber der wichtigste Punkt ist mMn. dann doch das Drucken. Da sieht PDFtk sehr geeignet für aus, danke für den Tipp! Insbesondere als ServerSoftware sieht es nach der perfekten Lösung aus.
Caldzera
User
Beiträge: 4
Registriert: Sonntag 19. Mai 2019, 10:51

Hallo zusammen

Ich konnte einen Kollegen für das Projekt gewinnen, welcher schon etwas mehr Erfahrungen mit Python hat. Gemeinsam haben wir nun folgendes hingekriegt:
- Export der Wordfiles mithilfe von VBA in eine PowerPoint und direkt auch in ein PDF, in welchem 1 Slide 1 Seite ist. (als Backup für unsere Macs, die ja kein VBA unterstützen, haben wir das Gleiche noch mit Python gemacht)
- Export der Slides auf 4x2 Slides pro Seite für den Druck.

Also eigentlich haben wir es "geschafft"… doch der Export in die Druckdatei bereitet uns noch etwas Kopfschmerzen, da wir die Reihenfolge nicht hinkriegen. Kurze Erläuterung:
- Angenommen, wir haben 16 Slides (also 8 Vorderseiten und 8 Rückseiten für insgesamt 8 Karteikarten)
- dann schreibt unser Script, stand aktuell, alle Slides in normaler Reihenfolge, also Seite 1 wären Slide:
1 - 2
3 - 4
5 - 6
7 - 8

Die Rückseite ist dann

9 - 10
11 - 12
13 - 14
15 - 16

Korrekt sollte es aber so sein:
1 - 2
3 - 4
5 - 6
7 - 8
und
10 - 9
12 - 11
14 - 13
16 - 15

Wir finden einfach keinen passenden Befehl dafür. Vielleicht hat ja jemand von euch eine Idee, der Code des converters wäre:

Code: Alles auswählen

import sys
import PyPDF2
from PyPDF2.pdf import PageObject
from reportlab.lib.pagesizes import A4

arg_list = sys.argv

reader = PyPDF2.PdfFileReader(open(f'{arg_list[1]}.pdf', 'rb'))
num_page = reader.getNumPages()

i = 0
x = 0
width, height = A4
arr_p = []
arr_n = []

writer = PyPDF2.PdfFileWriter()
starter_page = PageObject.createBlankPage(None, width, height)
translated_page = PageObject.createBlankPage(None, width, height)

while i < num_page:
    if i % 2 == 0:
        arr_p.append(i)
    else:
        arr_n.append(i)
    i += 1

while x < arr_p.__len__():
    p_page = reader.getPage(arr_p[x])
    n_page = reader.getPage(arr_n[x])
    if x % 8 == 1:
        starter_page.mergeTranslatedPage(p_page, 0, 630)
        translated_page.mergeTranslatedPage(n_page, 0, 630)
    elif x % 8 == 2:
        starter_page.mergeTranslatedPage(p_page, 300, 630)
        translated_page.mergeTranslatedPage(n_page, 300, 630)
    elif x % 8 == 3:
        starter_page.mergeTranslatedPage(p_page, 0, 420)
        translated_page.mergeTranslatedPage(n_page, 0, 420)
    elif x % 8 == 4:
        starter_page.mergeTranslatedPage(p_page, 300, 420)
        translated_page.mergeTranslatedPage(n_page, 300, 420)
    elif x % 8 == 5:
        starter_page.mergeTranslatedPage(p_page, 0, 210)
        translated_page.mergeTranslatedPage(n_page, 0, 210)
    elif x % 8 == 6:
        starter_page.mergeTranslatedPage(p_page, 300, 210)
        translated_page.mergeTranslatedPage(n_page, 300, 210)
    elif x % 8 == 7:
        starter_page.mergeTranslatedPage(p_page, 0, 0)
        translated_page.mergeTranslatedPage(n_page, 0, 0)
    elif x % 8 == 0:
        starter_page.mergeTranslatedPage(p_page, 300, 0)
        translated_page.mergeTranslatedPage(n_page, 300, 0)
    
    if x % 8 == 0:
        writer.addPage(translated_page)
        translated_page = PageObject.createBlankPage(None, width, height)
        starter_page.rotateCounterClockwise(180)
        writer.addPage(starter_page)
        starter_page = PageObject.createBlankPage(None, width, height)
    x += 1

with open(f"{arg_list[1]}_printready.pdf", 'wb') as f:
    writer.write(f)
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Wenn ihr eine andere Reihenfolge für die Ausdrucke wollt, dann müsst ihr doch nur die Positionsangaben für den Aufruf von mergeTranslatedPage() ändern. Habt ihr das schon versucht...?
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Caldzera: Ein paar Anmerkungen zu dem Quelltext:

”Magische” Methoden wie `__len__()` ruft man nicht direkt auf wenn es dafür einen anderen vorgesehenen Weg gibt. Dafür gibt es die `len()`-Funktion.

`sys.argv` an einen lokalen Namen zu binden macht nicht wirklich Sinn. Der Code wird dadurch weder leichter verständlich noch signifikant schneller. Grunddatentypen sollten auch nicht in Namen vorkommen. Wenn dann wäre so etwas wie `arguments` oder `commandline_arguments` ein Name der zumindest deutlicher macht was die historisch bedingte Abkürzung `argv` bedeutet. Da beim Zugriff jedes mal das zweite Element dieser Liste angesprochen wird, würde es Sinn machen diesen Wert an einen sprechenden Namen zu binden.

Dateien die man öffnet, sollte man auch wieder schliessen. Bei der Eingabedatei verlässt sich das Skript darauf dass das schon irgendwann automatisch passieren wird.

Variablen sollte man möglichst nahe an der Stelle definieren an der sie dann letztlich auch gebraucht werden und nicht irgendwo am Anfang in einem Block. Das macht es schwerer den Code nachzuvollziehen, Code in eine eigene Funktion heraus zu ziehen, und man übersieht so auch gerne mal Variablen die gar nicht mehr verwendet werden, wenn man Code an anderer Stelle ändert.

`arr_p` und `arr_n` sind fürchterliche Namen. Man muss raten was die Abkürzungen wohl bedeuten mögen. Bei `arr` rate ich mal das da jemand zu faul war `array` zu tippen. Das ist aber inhaltlich falsch, weil das an eine Liste gebunden ist und kein Array. Einer der Gründe warum solche Grunddatentypen nicht in Namen haben will, denn wenn man den Typ im Laufe der Programmentwicklung ändert, hat man falsche, irreführende Namen, oder man muss alle betroffenen Namen anpassen. Der Leser will eigentlich auch gar nicht wirklich wissen was das nun genau für ein Sequenztyp ist, sondern was die Werte darin bedeuten – und da ist mir beim besten Willen nicht eingefallen wofür `p` und `n` wohl stehen mögen. Wenn man sich den Code anschaut sind gute allgemeine Namen `even_page_numbers` und `odd_page_numbers`. Besser wäre natürlich wenn man an den Namen ablesen könnte um was die jeweilige *Bedeutung* des jeweiligen Seitentyps im Kontext dieses Programms ist.

Die beiden ``while``-Schleifen sind eigentlich ``for``-Schleifen. Schauen wir uns die erste mal genauer an:

Code: Alles auswählen

        page_count = reader.getNumPages()
        even_page_numbers = []
        odd_page_numbers = []
        i = 0
        while i < page_count:
            if i % 2 == 0:
                even_page_numbers.append(i)
            else:
                odd_page_numbers.append(i)
            i += 1
Wenn man da eine ``for``-Schleife draus macht, wird es schon etwas einfacher:

Code: Alles auswählen

        even_page_numbers = []
        odd_page_numbers = []
        for i in range(reader.getNumPages()):
            if i % 2 == 0:
                even_page_numbers.append(i)
            else:
                odd_page_numbers.append(i)
Man könnte aber auch ganz ohne die ``for``-Schleife einfach gleich mit `list()` und `range()` entsprechende Sequenzen erstellen:

Code: Alles auswählen

        page_count = reader.getNumPages()
        even_page_numbers = range(0, page_count, 2)
        odd_page_numbers = range(1, page_count, 2)
Allerdings braucht man diese Listen mit den doch sehr langweiligen Werten überhaupt gar nicht, weil man das doch genau so gut einfach in der nächsten Schleife berechnen kann was da ursprünglich in zwei Listen gesteckt wurde.

Warum `x`? Hier hätte man doch einfach wieder `i` nehmen können. Beziehungsweise einen Namen der verrät was der Wert bedeutet. Ich würde da in der zweiten Schleife, was ja auch eine ``for``-Schleife ist, gleich über die geraden Seitenindizes iterieren.

Wenn man den Code in der zweiten Schleife etwas anders arrangiert, kann man die Wiederholung des Codes zu erstellen von leeren Seiten nur einmal schreiben, statt zweimal.

Die ganzen Zweige mit den `mergeTranslatedPage()`-Aufrufen sind nahezu identisch. Das einzige was sich in Abhängigkeit vom Divisionsrest ändert sind die Koordinaten. Die könnte man in eine Liste stecken und sich so die ganzen ``if``/``elif``-Zweige sparen.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import sys

from PyPDF2.pdf import PageObject, PdfFileReader, PdfFileWriter
from reportlab.lib.pagesizes import A4


def main():
    pdf_filename_stem = sys.argv[1]

    with open(f'{pdf_filename_stem}.pdf', 'rb') as pdf_file:
        reader = PdfFileReader(pdf_file)
        writer = PdfFileWriter()
        coordinates = [
            (300, 0), (0, 630),
            (300, 630), (0, 420),
            (300, 420), (0, 210),
            (300, 210), (0, 0),
        ]
        width, height = A4
        for even_page_index in range(0, reader.getNumPages(), 2):
            if even_page_index % len(coordinates) == 0:
                if even_page_index != 0:
                    writer.addPage(translated_page)
                    starter_page.rotateCounterClockwise(180)
                    writer.addPage(starter_page)
                
                starter_page = PageObject.createBlankPage(None, width, height)
                translated_page = PageObject.createBlankPage(None, width, height)
            # 
            # TODO Find better names without those cryptic `p` and `n`.
            # 
            x, y = coordinates[even_page_index % len(coordinates)]
            p_page = reader.getPage(even_page_index)
            starter_page.mergeTranslatedPage(p_page, x, y)
            n_page = reader.getPage(even_page_index + 1)
            translated_page.mergeTranslatedPage(n_page, x, y)


    with open(f'{pdf_filename_stem}_printready.pdf', 'wb') as pdf_file:
        writer.write(pdf_file)


if __name__ == '__main__':
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Schleife ist jetzt aber ziemlich verquer. Erst wird geprüft, ob eine Seite voll ist, um sie dann zu schreiben und eine neue leere Seite zu erzeugen. Eigentlich will man ja das Programm so aufbauen, wie es auch abläuft. Erst leere Seiten erzeugen, dann füllen, dann schreiben. Die letzte, nicht ganz gefüllte Seite wird nie geschrieben.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Sirius3: Das ist genau so verquer wie vorher auch mit dem gleichen Problemen — Seitenanzahl in der Quelle muss gerade und durch 16 teilbar sein, sonst gibt es Probleme beim lesen einer nicht-existierenden Seite oder es wird nicht alles geschrieben. Ich würde das letztendlich ordentlich auf mehrere Funktionen aufteilen, so mit Generatorfunktionen und (more_)itertools, aber ich wollte halt nicht zu viel auf einmal ändern aber auf jeden Fall die Codewiederholung für das Erstellen der leeren Seiten beseitigen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Immer noch ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import sys
from contextlib import closing

from more_itertools import chunked, pairwise
from PyPDF2.pdf import PageObject, PdfFileReader, PdfFileWriter
from reportlab.lib.pagesizes import A4


def iter_pdf_pages(filename):
    with open(filename, 'rb') as pdf_file:
        reader = PdfFileReader(pdf_file)    
        for i in range(reader.getNumPages()):
            yield reader.getPage(i)


def iter_merged_pages(input_pages, coordinates, page_size):
    width, height = page_size
    for pages in chunked(input_pages, len(coordinates) * 2):
        starter_page = PageObject.createBlankPage(None, width, height)
        translated_page = PageObject.createBlankPage(None, width, height)
        for (p_page, n_page), (x, y) in zip(pairwise(pages), coordinates):
            starter_page.mergeTranslatedPage(p_page, x, y)
            translated_page.mergeTranslatedPage(n_page, x, y)
        starter_page.rotateCounterClockwise(180)
        yield translated_page
        yield starter_page


def main():
    pdf_filename_stem = sys.argv[1]
    coordinates = [
        (300, 0), (0, 630),
        (300, 630), (0, 420),
        (300, 420), (0, 210),
        (300, 210), (0, 0),
    ]
    with closing(iter_pdf_pages(f'{pdf_filename_stem}.pdf')) as input_pages:
        writer = PdfFileWriter()
        for output_page in iter_merged_pages(input_pages, coordinates, A4):
            writer.addPage(output_page)
            
    with open(f'{pdf_filename_stem}_printready.pdf', 'wb') as pdf_file:
        writer.write(pdf_file)


if __name__ == '__main__':
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten