MikeWelsOOE
Cadet 1st Year
- Registriert
- Dez. 2024
- Beiträge
- 10
Hallo liebe ComputerBase-Community,
ich arbeite an einem kleinen, clientseitigen Web-Tool und bräuchte eure Unterstützung. Mein Ziel ist es, einen einfachen Buch-Konfigurator zu erstellen, der basierend auf Nutzereingaben (Titel, Autor, Kapitelanzahl, Wörter pro Kapitel) ein DOCX-Dokument generiert und zum Download anbietet.
Ich habe den HTML-Code (inkl. CSS und JavaScript) von einem AI-Modell erstellen lassen, das auf die docx.js- und FileSaver.js-Bibliotheken setzt. Die Idee war, das Ganze komplett im Browser, also ohne Backend und API-Keys, zu realisieren.
Leider stoße ich auf ein hartnäckiges Problem: Beim Klick auf den "Generieren"-Button passiert nichts, und in der Browser-Konsole erhalte ich durchgehend die Fehlermeldung: Uncaught ReferenceError: docx is not defined. Dies deutet darauf hin, dass die docx-Bibliothek nicht korrekt geladen oder zur Verfügung steht, wenn mein Skript versucht, darauf zuzugreifen.
Ich habe bereits versucht:
Könnte sich jemand von euch den Code bitte ansehen und mir einen Tipp geben, was die Ursache für diesen ReferenceError sein könnte und wie ich das Tool zum Laufen bringe? Für jede Hilfe wäre ich sehr dankbar!
Hier der HTML Code :
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Buch Konfigurator & DOCX Export</title>
<style>
body {
background-color: #111;
color: #FFA500;
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
}
.container {
background-color: #222;
padding: 30px;
border-radius: 10px;
box-shadow: 0 0 15px rgba(255, 165, 0, 0.5);
width: 90%;
max-width: 700px; /* Etwas breiter für mehr Inhalt */
text-align: center;
}
h1 {
color: #FFA500;
text-shadow: 2px 2px 4px #000;
}
label {
display: block;
margin-top: 20px;
margin-bottom: 8px;
font-weight: bold;
color: #FFD700;
text-align: left;
}
input[type="number"],
input[type="text"] {
width: calc(100% - 22px);
padding: 10px;
margin-bottom: 20px;
border: 1px solid #FFA500;
border-radius: 5px;
background-color: #333;
color: #FFA500;
font-size: 1em;
}
input[type="number"]:focus,
input[type="text"]:focus {
outline: none;
border-color: #FFD700;
box-shadow: 0 0 8px rgba(255, 165, 0, 0.7);
}
button {
background-color: #FFA500;
color: #111;
border: none;
padding: 12px 25px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 1.1em;
font-weight: bold;
margin-top: 30px;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s ease, transform 0.1s ease;
}
button:hover {
background-color: #FFC14D;
}
button:active {
transform: scale(0.98);
}
.footer-note {
margin-top: 40px;
font-size: 0.9em;
color: #ccc;
}
#status {
margin-top: 20px;
font-size: 0.9em;
color: #FFD700;
min-height: 20px;
}
</style>
</head>
<body>
<div class="container">
<h1>📖 Buch Konfigurator & DOCX Export</h1>
<div>
<label for="buchTitel">Titel des Buches:</label>
<input type="text" id="buchTitel" name="buchTitel" placeholder="z.B. Die Chroniken von Eldoria">
</div>
<div>
<label for="autorName">Autor:</label>
<input type="text" id="autorName" name="autorName" placeholder="Ihr Name oder Pseudonym">
</div>
<div>
<label for="kapitelAnzahl">Wieviele Kapitel möchtest du (max. 120 für Demo)?</label>
<input type="number" id="kapitelAnzahl" name="kapitelAnzahl" min="1" max="120" placeholder="z.B. 10">
</div>
<div>
<label for="woerterProKapitel">Ungefähre Wörter pro Kapitel (für Platzhaltertext):</label>
<input type="number" id="woerterProKapitel" name="woerterProKapitel" min="50" max="2000" placeholder="z.B. 500">
</div>
<button type="button" id="generierenButton">Buch als DOCX generieren</button>
<div id="status"></div>
<div class="footer-note">
<p>Webseiten-Code generiert mit einer KOSTENLOSEN AI. Ohne API KEY. Deutsche Sprache. In Echtzeit.
<br>Die Buchinhaltsgenerierung verwendet Platzhaltertext.
</p>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
<script src="https://unpkg.com/docx@8.5.0/build/index.js"></script>
<script>
// Zugriff auf die docx-Bibliothek (wird global durch das Skript-Tag oben verfügbar)
const { Document, Packer, Paragraph, TextRun, HeadingLevel, AlignmentType, PageBreak } = docx;
const buchTitelInput = document.getElementById('buchTitel');
const autorNameInput = document.getElementById('autorName');
const kapitelAnzahlInput = document.getElementById('kapitelAnzahl');
const woerterProKapitelInput = document.getElementById('woerterProKapitel');
const generierenButton = document.getElementById('generierenButton');
const statusDiv = document.getElementById('status');
generierenButton.addEventListener('click', async function() {
const titel = buchTitelInput.value.trim();
const autor = autorNameInput.value.trim() || "Unbekannter Autor";
const anzahlKapitel = parseInt(kapitelAnzahlInput.value);
const woerterProKapitel = parseInt(woerterProKapitelInput.value);
if (!titel) {
alert("Bitte geben Sie einen Titel für das Buch ein.");
return;
}
if (isNaN(anzahlKapitel) || anzahlKapitel < 1 || anzahlKapitel > 120) { // Max 120 für Demo
alert("Bitte geben Sie eine gültige Anzahl an Kapiteln ein (1-120 für diese Demo).");
return;
}
if (isNaN(woerterProKapitel) || woerterProKapitel < 50 || woerterProKapitel > 2000) { // Max 2000 für Demo
alert("Bitte geben Sie eine gültige Anzahl an Wörtern pro Kapitel ein (50-2000 für diese Demo).");
return;
}
statusDiv.textContent = "Generiere DOCX, bitte warten...";
generierenButton.disabled = true;
try {
// Generiere genug Lorem Ipsum für alle Kapitel
const loremBase = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. ";
const loremIpsum = loremBase.repeat(Math.ceil(woerterProKapitel / (loremBase.split(" ").length / 5)) + 5); // Sicherstellen, dass genug Text da ist
const sections = [];
// 1. Titelseite
sections.push({
properties: {
page: {
size: { width: docx.TabStopPosition.MAX, height: docx.TabStopPosition.MAX }, // A4 default
margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } // 1 inch = 1440 TWIPs
}
},
children: [
new Paragraph({ text: "" }), // Leerraum oben
new Paragraph({ text: "" }),
new Paragraph({ text: "" }),
new Paragraph({
children: [new TextRun({ text: titel, bold: true, size: 48 })], // Titelgröße: 24pt * 2 = 48 half-points
alignment: AlignmentType.CENTER,
spacing: { after: 400 }
}),
new Paragraph({ text: "" }),
new Paragraph({
children: [new TextRun({ text:
alignment: AlignmentType.CENTER,
}),
new Paragraph({
children: [new TextRun({ text: autor, size: 36 })],
alignment: AlignmentType.CENTER,
spacing: { after: 2000 } // Mehr Platz nach dem Autor
}),
]
});
// Seite für Impressum (Platzhalter) - Startet auf neuer Seite
sections[0].children.push(new Paragraph({ children: [new PageBreak()] }));
sections[0].children.push(
new Paragraph({
children: [new TextRun({ text: "Impressum", bold: true, size: 28 })],
heading: HeadingLevel.HEADING_2,
spacing: { after: 200 }
}),
new Paragraph({ text:
new Paragraph({ text: "Alle Rechte vorbehalten.", size: 24 }),
new Paragraph({ text: "Verlag: Selbstverlag (Beispiel)", size: 24 }),
new Paragraph({ text: "Druck: epubli GmbH, Berlin (Beispiel)", size: 24 }),
new Paragraph({ text: "ISBN: (Hier Ihre ISBN eintragen, falls vorhanden)", size: 24 })
);
// 2. Kapitel
for (let i = 1; i <= anzahlKapitel; i++) {
const kapitelInhalt = [];
// Jedes Kapitel beginnt auf einer neuen Seite (außer das erste nach dem Impressum)
// Da die Titelseite und das Impressum bereits in sections[0] sind, fügen wir PageBreaks zu den Kapiteln hinzu.
// Das erste Kapitel nach dem Impressum braucht einen PageBreak, die folgenden Kapitel dann auch.
kapitelInhalt.push(new Paragraph({ children: [new PageBreak()] }));
kapitelInhalt.push(new Paragraph({
children: [new TextRun({ text:
heading: HeadingLevel.HEADING_1,
alignment: AlignmentType.CENTER,
spacing: { before: 400, after: 300 }
}));
// Füge mehrere Absätze mit Platzhaltertext hinzu
// Versuche, die Wörter gleichmäßig auf mehrere Absätze zu verteilen.
const wordsPerParagraphTarget = woerterProKapitel / 3; // Ziel sind 3 Absätze pro Kapitel, passe dies nach Bedarf an
let currentWordCount = 0;
let paragraphCount = 0;
while (currentWordCount < woerterProKapitel && paragraphCount < 20) { // Max 20 Absätze zur Sicherheit
let wordsInThisParagraph = Math.min(wordsPerParagraphTarget, woerterProKapitel - currentWordCount);
// Finde das Ende eines Satzes in loremIpsum, um den Text sauber abzuschneiden
let endIndex = 0;
let tempText = loremIpsum;
let words = tempText.split(/\s+/); // Split by any whitespace
let actualWords = 0;
for (let j = 0; j < words.length; j++) {
actualWords++;
if (actualWords >= wordsInThisParagraph && words[j].endsWith(".")) {
endIndex = tempText.indexOf(words[j]) + words[j].length;
break;
}
endIndex = tempText.indexOf(words[j]) + words[j].length;
}
if (endIndex === 0) endIndex = loremIpsum.length; // Fallback falls kein Punkt gefunden
let paragraphText = loremIpsum.substring(0, endIndex);
kapitelInhalt.push(new Paragraph({
children: [new TextRun({ text: paragraphText, size: 24 })], // 12pt * 2
spacing: { after: 120, line: 360 }, // 1.5 line spacing (360 TWIPs)
indent: { firstLine: 720 } // Erster Zeileneinzug: 0.5 inch = 720 TWIPs
}));
currentWordCount += paragraphText.split(" ").length;
paragraphCount++;
// Schneide den verwendeten Text vom loremIpsum ab, um für den nächsten Absatz zu verwenden
loremIpsum = loremIpsum.substring(endIndex).trim();
if (loremIpsum.length < 50) loremIpsum += loremBase.repeat(5); // Fülle auf, falls leer
}
// Die Kapitel-Inhalte werden zur ersten Sektion hinzugefügt, da DOCX-Abschnitte für große Layout-Änderungen sind,
// und die Kapitel innerhalb einer einzigen Fließtext-Sektion mit Seitenumbrüchen erstellt werden.
sections[0].children.push(...kapitelInhalt);
}
const doc = new Document({
creator: autor,
title: titel,
description: "Generiert mit dem Buch Konfigurator",
styles: {
default: {
document: {
run: {
font: "Arial", // Standard Schriftart für epubli oft Arial oder Times New Roman
size: 24, // 12pt (docx.js verwendet half-points)
},
paragraph: {
spacing: { line: 360 } // Entspricht ca. 1.5 Zeilenabstand
}
},
heading1: {
run: { font: "Arial", size: 32, bold: true }, // 16pt
paragraph: { spacing: { before: 240, after: 120 } }
},
heading2: {
run: { font: "Arial", size: 28, bold: true }, // 14pt
paragraph: { spacing: { before: 200, after: 100 } }
}
}
},
sections: sections // Das gesamte Dokument besteht aus diesen Sektionen
});
Packer.toBlob(doc).then(blob => {
// Sanitize filename
const safeTitel = titel.replace(/[^a-z0-9_ ]/gi, '').replace(/ /g, '');
saveAs(blob,
statusDiv.textContent = "DOCX-Datei erfolgreich generiert und Download gestartet!";
}).catch(err => {
console.error(err);
statusDiv.textContent = "Fehler beim Erstellen der DOCX-Datei.";
alert("Fehler beim Erstellen der DOCX-Datei: " + err.message);
});
} catch (e) {
console.error(e);
statusDiv.textContent = "Ein unerwarteter Fehler ist aufgetreten.";
alert("Ein Fehler ist aufgetreten: " + e.message);
} finally {
generierenButton.disabled = false;
}
});
</script>
</body>
</html>
Vielen Dank im Voraus!
ich arbeite an einem kleinen, clientseitigen Web-Tool und bräuchte eure Unterstützung. Mein Ziel ist es, einen einfachen Buch-Konfigurator zu erstellen, der basierend auf Nutzereingaben (Titel, Autor, Kapitelanzahl, Wörter pro Kapitel) ein DOCX-Dokument generiert und zum Download anbietet.
Ich habe den HTML-Code (inkl. CSS und JavaScript) von einem AI-Modell erstellen lassen, das auf die docx.js- und FileSaver.js-Bibliotheken setzt. Die Idee war, das Ganze komplett im Browser, also ohne Backend und API-Keys, zu realisieren.
Leider stoße ich auf ein hartnäckiges Problem: Beim Klick auf den "Generieren"-Button passiert nichts, und in der Browser-Konsole erhalte ich durchgehend die Fehlermeldung: Uncaught ReferenceError: docx is not defined. Dies deutet darauf hin, dass die docx-Bibliothek nicht korrekt geladen oder zur Verfügung steht, wenn mein Skript versucht, darauf zuzugreifen.
Ich habe bereits versucht:
- Die Internetverbindung geprüft.
- Die CDN-Links (https://unpkg.com/docx@8.5.0/build/index.js und https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js) direkt im Browser auf Erreichbarkeit getestet – sie laden dort.
- Browser-Cache geleert und es im Inkognito-Modus versucht.
- Die <script>-Tags ans Ende des <body>-Bereichs verschoben, um die Lade-Reihenfolge zu optimieren.
Könnte sich jemand von euch den Code bitte ansehen und mir einen Tipp geben, was die Ursache für diesen ReferenceError sein könnte und wie ich das Tool zum Laufen bringe? Für jede Hilfe wäre ich sehr dankbar!
Hier der HTML Code :
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Buch Konfigurator & DOCX Export</title>
<style>
body {
background-color: #111;
color: #FFA500;
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
}
.container {
background-color: #222;
padding: 30px;
border-radius: 10px;
box-shadow: 0 0 15px rgba(255, 165, 0, 0.5);
width: 90%;
max-width: 700px; /* Etwas breiter für mehr Inhalt */
text-align: center;
}
h1 {
color: #FFA500;
text-shadow: 2px 2px 4px #000;
}
label {
display: block;
margin-top: 20px;
margin-bottom: 8px;
font-weight: bold;
color: #FFD700;
text-align: left;
}
input[type="number"],
input[type="text"] {
width: calc(100% - 22px);
padding: 10px;
margin-bottom: 20px;
border: 1px solid #FFA500;
border-radius: 5px;
background-color: #333;
color: #FFA500;
font-size: 1em;
}
input[type="number"]:focus,
input[type="text"]:focus {
outline: none;
border-color: #FFD700;
box-shadow: 0 0 8px rgba(255, 165, 0, 0.7);
}
button {
background-color: #FFA500;
color: #111;
border: none;
padding: 12px 25px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 1.1em;
font-weight: bold;
margin-top: 30px;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s ease, transform 0.1s ease;
}
button:hover {
background-color: #FFC14D;
}
button:active {
transform: scale(0.98);
}
.footer-note {
margin-top: 40px;
font-size: 0.9em;
color: #ccc;
}
#status {
margin-top: 20px;
font-size: 0.9em;
color: #FFD700;
min-height: 20px;
}
</style>
</head>
<body>
<div class="container">
<h1>📖 Buch Konfigurator & DOCX Export</h1>
<div>
<label for="buchTitel">Titel des Buches:</label>
<input type="text" id="buchTitel" name="buchTitel" placeholder="z.B. Die Chroniken von Eldoria">
</div>
<div>
<label for="autorName">Autor:</label>
<input type="text" id="autorName" name="autorName" placeholder="Ihr Name oder Pseudonym">
</div>
<div>
<label for="kapitelAnzahl">Wieviele Kapitel möchtest du (max. 120 für Demo)?</label>
<input type="number" id="kapitelAnzahl" name="kapitelAnzahl" min="1" max="120" placeholder="z.B. 10">
</div>
<div>
<label for="woerterProKapitel">Ungefähre Wörter pro Kapitel (für Platzhaltertext):</label>
<input type="number" id="woerterProKapitel" name="woerterProKapitel" min="50" max="2000" placeholder="z.B. 500">
</div>
<button type="button" id="generierenButton">Buch als DOCX generieren</button>
<div id="status"></div>
<div class="footer-note">
<p>Webseiten-Code generiert mit einer KOSTENLOSEN AI. Ohne API KEY. Deutsche Sprache. In Echtzeit.
<br>Die Buchinhaltsgenerierung verwendet Platzhaltertext.
</p>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
<script src="https://unpkg.com/docx@8.5.0/build/index.js"></script>
<script>
// Zugriff auf die docx-Bibliothek (wird global durch das Skript-Tag oben verfügbar)
const { Document, Packer, Paragraph, TextRun, HeadingLevel, AlignmentType, PageBreak } = docx;
const buchTitelInput = document.getElementById('buchTitel');
const autorNameInput = document.getElementById('autorName');
const kapitelAnzahlInput = document.getElementById('kapitelAnzahl');
const woerterProKapitelInput = document.getElementById('woerterProKapitel');
const generierenButton = document.getElementById('generierenButton');
const statusDiv = document.getElementById('status');
generierenButton.addEventListener('click', async function() {
const titel = buchTitelInput.value.trim();
const autor = autorNameInput.value.trim() || "Unbekannter Autor";
const anzahlKapitel = parseInt(kapitelAnzahlInput.value);
const woerterProKapitel = parseInt(woerterProKapitelInput.value);
if (!titel) {
alert("Bitte geben Sie einen Titel für das Buch ein.");
return;
}
if (isNaN(anzahlKapitel) || anzahlKapitel < 1 || anzahlKapitel > 120) { // Max 120 für Demo
alert("Bitte geben Sie eine gültige Anzahl an Kapiteln ein (1-120 für diese Demo).");
return;
}
if (isNaN(woerterProKapitel) || woerterProKapitel < 50 || woerterProKapitel > 2000) { // Max 2000 für Demo
alert("Bitte geben Sie eine gültige Anzahl an Wörtern pro Kapitel ein (50-2000 für diese Demo).");
return;
}
statusDiv.textContent = "Generiere DOCX, bitte warten...";
generierenButton.disabled = true;
try {
// Generiere genug Lorem Ipsum für alle Kapitel
const loremBase = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. ";
const loremIpsum = loremBase.repeat(Math.ceil(woerterProKapitel / (loremBase.split(" ").length / 5)) + 5); // Sicherstellen, dass genug Text da ist
const sections = [];
// 1. Titelseite
sections.push({
properties: {
page: {
size: { width: docx.TabStopPosition.MAX, height: docx.TabStopPosition.MAX }, // A4 default
margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } // 1 inch = 1440 TWIPs
}
},
children: [
new Paragraph({ text: "" }), // Leerraum oben
new Paragraph({ text: "" }),
new Paragraph({ text: "" }),
new Paragraph({
children: [new TextRun({ text: titel, bold: true, size: 48 })], // Titelgröße: 24pt * 2 = 48 half-points
alignment: AlignmentType.CENTER,
spacing: { after: 400 }
}),
new Paragraph({ text: "" }),
new Paragraph({
children: [new TextRun({ text:
von
, size: 28 })],alignment: AlignmentType.CENTER,
}),
new Paragraph({
children: [new TextRun({ text: autor, size: 36 })],
alignment: AlignmentType.CENTER,
spacing: { after: 2000 } // Mehr Platz nach dem Autor
}),
]
});
// Seite für Impressum (Platzhalter) - Startet auf neuer Seite
sections[0].children.push(new Paragraph({ children: [new PageBreak()] }));
sections[0].children.push(
new Paragraph({
children: [new TextRun({ text: "Impressum", bold: true, size: 28 })],
heading: HeadingLevel.HEADING_2,
spacing: { after: 200 }
}),
new Paragraph({ text:
© ${new Date().getFullYear()} ${autor}
, size: 24 }),new Paragraph({ text: "Alle Rechte vorbehalten.", size: 24 }),
new Paragraph({ text: "Verlag: Selbstverlag (Beispiel)", size: 24 }),
new Paragraph({ text: "Druck: epubli GmbH, Berlin (Beispiel)", size: 24 }),
new Paragraph({ text: "ISBN: (Hier Ihre ISBN eintragen, falls vorhanden)", size: 24 })
);
// 2. Kapitel
for (let i = 1; i <= anzahlKapitel; i++) {
const kapitelInhalt = [];
// Jedes Kapitel beginnt auf einer neuen Seite (außer das erste nach dem Impressum)
// Da die Titelseite und das Impressum bereits in sections[0] sind, fügen wir PageBreaks zu den Kapiteln hinzu.
// Das erste Kapitel nach dem Impressum braucht einen PageBreak, die folgenden Kapitel dann auch.
kapitelInhalt.push(new Paragraph({ children: [new PageBreak()] }));
kapitelInhalt.push(new Paragraph({
children: [new TextRun({ text:
Kapitel ${i}
, bold: true, size: 32 })], // 16pt * 2heading: HeadingLevel.HEADING_1,
alignment: AlignmentType.CENTER,
spacing: { before: 400, after: 300 }
}));
// Füge mehrere Absätze mit Platzhaltertext hinzu
// Versuche, die Wörter gleichmäßig auf mehrere Absätze zu verteilen.
const wordsPerParagraphTarget = woerterProKapitel / 3; // Ziel sind 3 Absätze pro Kapitel, passe dies nach Bedarf an
let currentWordCount = 0;
let paragraphCount = 0;
while (currentWordCount < woerterProKapitel && paragraphCount < 20) { // Max 20 Absätze zur Sicherheit
let wordsInThisParagraph = Math.min(wordsPerParagraphTarget, woerterProKapitel - currentWordCount);
// Finde das Ende eines Satzes in loremIpsum, um den Text sauber abzuschneiden
let endIndex = 0;
let tempText = loremIpsum;
let words = tempText.split(/\s+/); // Split by any whitespace
let actualWords = 0;
for (let j = 0; j < words.length; j++) {
actualWords++;
if (actualWords >= wordsInThisParagraph && words[j].endsWith(".")) {
endIndex = tempText.indexOf(words[j]) + words[j].length;
break;
}
endIndex = tempText.indexOf(words[j]) + words[j].length;
}
if (endIndex === 0) endIndex = loremIpsum.length; // Fallback falls kein Punkt gefunden
let paragraphText = loremIpsum.substring(0, endIndex);
kapitelInhalt.push(new Paragraph({
children: [new TextRun({ text: paragraphText, size: 24 })], // 12pt * 2
spacing: { after: 120, line: 360 }, // 1.5 line spacing (360 TWIPs)
indent: { firstLine: 720 } // Erster Zeileneinzug: 0.5 inch = 720 TWIPs
}));
currentWordCount += paragraphText.split(" ").length;
paragraphCount++;
// Schneide den verwendeten Text vom loremIpsum ab, um für den nächsten Absatz zu verwenden
loremIpsum = loremIpsum.substring(endIndex).trim();
if (loremIpsum.length < 50) loremIpsum += loremBase.repeat(5); // Fülle auf, falls leer
}
// Die Kapitel-Inhalte werden zur ersten Sektion hinzugefügt, da DOCX-Abschnitte für große Layout-Änderungen sind,
// und die Kapitel innerhalb einer einzigen Fließtext-Sektion mit Seitenumbrüchen erstellt werden.
sections[0].children.push(...kapitelInhalt);
}
const doc = new Document({
creator: autor,
title: titel,
description: "Generiert mit dem Buch Konfigurator",
styles: {
default: {
document: {
run: {
font: "Arial", // Standard Schriftart für epubli oft Arial oder Times New Roman
size: 24, // 12pt (docx.js verwendet half-points)
},
paragraph: {
spacing: { line: 360 } // Entspricht ca. 1.5 Zeilenabstand
}
},
heading1: {
run: { font: "Arial", size: 32, bold: true }, // 16pt
paragraph: { spacing: { before: 240, after: 120 } }
},
heading2: {
run: { font: "Arial", size: 28, bold: true }, // 14pt
paragraph: { spacing: { before: 200, after: 100 } }
}
}
},
sections: sections // Das gesamte Dokument besteht aus diesen Sektionen
});
Packer.toBlob(doc).then(blob => {
// Sanitize filename
const safeTitel = titel.replace(/[^a-z0-9_ ]/gi, '').replace(/ /g, '');
saveAs(blob,
${safeTitel}.docx
); // FileSaver.js FunktionstatusDiv.textContent = "DOCX-Datei erfolgreich generiert und Download gestartet!";
}).catch(err => {
console.error(err);
statusDiv.textContent = "Fehler beim Erstellen der DOCX-Datei.";
alert("Fehler beim Erstellen der DOCX-Datei: " + err.message);
});
} catch (e) {
console.error(e);
statusDiv.textContent = "Ein unerwarteter Fehler ist aufgetreten.";
alert("Ein Fehler ist aufgetreten: " + e.message);
} finally {
generierenButton.disabled = false;
}
});
</script>
</body>
</html>
Vielen Dank im Voraus!