File size: 8,171 Bytes
342973b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
"""
PDF Generator - Generate professional PDF documents
"""

import io
from typing import Dict, List, Optional
from datetime import datetime
import logging

logger = logging.getLogger(__name__)


class PDFGenerator:
    """
    Generate professional PDF documents with formatting, headers, footers, and citations.
    """

    def __init__(self):
        """Initialize PDF generator."""
        self.page_size = "A4"
        self.margins = {"top": 1.0, "bottom": 1.0, "left": 1.0, "right": 1.0}
        self.font_name = "Arial"
        self.font_size = 12
        self.line_spacing = 1.5

    def generate_pdf(
        self,
        title: str,
        content: Dict[str, str],
        author: str = "AI Academic Suite",
        include_toc: bool = True,
        include_citations: bool = False,
        citations: List[str] = None,
    ) -> bytes:
        """
        Generate PDF document.

        Args:
            title: Document title
            content: Dictionary of section titles and content
            author: Document author
            include_toc: Include table of contents
            include_citations: Include bibliography
            citations: List of citations

        Returns:
            PDF bytes
        """
        try:
            from reportlab.lib.pagesizes import A4
            from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
            from reportlab.lib.units import inch
            from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak, Table, TableStyle
            from reportlab.lib import colors

            # Create PDF in memory
            pdf_buffer = io.BytesIO()
            doc = SimpleDocTemplate(
                pdf_buffer,
                pagesize=A4,
                rightMargin=1 * inch,
                leftMargin=1 * inch,
                topMargin=1 * inch,
                bottomMargin=1 * inch,
            )

            # Create story (content)
            story = []

            # Add title page
            story.extend(self._create_title_page(title, author))
            story.append(PageBreak())

            # Add table of contents
            if include_toc:
                story.extend(self._create_toc(content))
                story.append(PageBreak())

            # Add sections
            styles = getSampleStyleSheet()
            for section_title, section_content in content.items():
                story.append(Paragraph(section_title, styles["Heading1"]))
                story.append(Spacer(1, 0.2 * inch))

                # Split content into paragraphs
                paragraphs = section_content.split("\n\n")
                for para_text in paragraphs:
                    if para_text.strip():
                        story.append(Paragraph(para_text, styles["Normal"]))
                        story.append(Spacer(1, 0.1 * inch))

                story.append(Spacer(1, 0.3 * inch))

            # Add bibliography
            if include_citations and citations:
                story.append(PageBreak())
                story.append(Paragraph("References", styles["Heading1"]))
                story.append(Spacer(1, 0.2 * inch))

                for citation in citations:
                    story.append(Paragraph(citation, styles["Normal"]))
                    story.append(Spacer(1, 0.1 * inch))

            # Build PDF
            doc.build(story)
            pdf_buffer.seek(0)
            return pdf_buffer.getvalue()

        except ImportError:
            logger.warning("reportlab not available, using fallback")
            return self._generate_pdf_fallback(title, content)

    def _create_title_page(self, title: str, author: str) -> list:
        """Create title page elements."""
        try:
            from reportlab.lib.styles import getSampleStyleSheet
            from reportlab.platypus import Paragraph, Spacer
            from reportlab.lib.units import inch

            styles = getSampleStyleSheet()
            story = []

            story.append(Spacer(1, 1 * inch))
            story.append(Paragraph(title, styles["Title"]))
            story.append(Spacer(1, 0.5 * inch))
            story.append(Paragraph(f"By {author}", styles["Normal"]))
            story.append(Spacer(1, 0.2 * inch))
            story.append(Paragraph(datetime.now().strftime("%B %d, %Y"), styles["Normal"]))

            return story
        except:
            return []

    def _create_toc(self, content: Dict[str, str]) -> list:
        """Create table of contents."""
        try:
            from reportlab.lib.styles import getSampleStyleSheet
            from reportlab.platypus import Paragraph, Spacer
            from reportlab.lib.units import inch

            styles = getSampleStyleSheet()
            story = []

            story.append(Paragraph("Table of Contents", styles["Heading1"]))
            story.append(Spacer(1, 0.3 * inch))

            for i, section in enumerate(content.keys(), 1):
                story.append(Paragraph(f"{i}. {section}", styles["Normal"]))
                story.append(Spacer(1, 0.1 * inch))

            return story
        except:
            return []

    def _generate_pdf_fallback(self, title: str, content: Dict[str, str]) -> bytes:
        """Fallback PDF generation using fpdf2."""
        try:
            from fpdf import FPDF

            pdf = FPDF()
            pdf.add_page()
            pdf.set_font("Arial", "B", 16)

            # Title
            pdf.cell(0, 10, title, ln=True, align="C")
            pdf.ln(10)

            # Content
            pdf.set_font("Arial", "", 12)
            for section_title, section_content in content.items():
                pdf.set_font("Arial", "B", 14)
                pdf.cell(0, 10, section_title, ln=True)
                pdf.set_font("Arial", "", 12)

                # Add content with text wrapping
                for line in section_content.split("\n"):
                    pdf.multi_cell(0, 10, line)
                pdf.ln(5)

            return pdf.output(dest="S").encode("latin-1")

        except ImportError:
            # Fallback to simple text
            return f"PDF Generation Failed - {title}".encode("utf-8")

    def add_header_footer(self, pdf_content: bytes, header: str = "", footer: str = "") -> bytes:
        """
        Add header and footer to PDF.

        Args:
            pdf_content: Original PDF bytes
            header: Header text
            footer: Footer text

        Returns:
            PDF with header/footer
        """
        try:
            from PyPDF2 import PdfReader, PdfWriter
            from reportlab.pdfgen import canvas
            from reportlab.lib.pagesizes import A4
            import io

            # Create header/footer canvas
            header_buffer = io.BytesIO()
            c = canvas.Canvas(header_buffer, pagesize=A4)
            c.setFont("Arial", 10)

            if header:
                c.drawString(50, 750, header)
            if footer:
                c.drawString(50, 30, footer)

            c.save()
            header_buffer.seek(0)

            # Merge with original PDF
            reader = PdfReader(io.BytesIO(pdf_content))
            writer = PdfWriter()

            for page in reader.pages:
                page.merge_page(PdfReader(header_buffer).pages[0])
                writer.add_page(page)

            output_buffer = io.BytesIO()
            writer.write(output_buffer)
            output_buffer.seek(0)

            return output_buffer.getvalue()

        except:
            return pdf_content  # Return original if header/footer fails

    def extract_text_from_pdf(self, pdf_content: bytes) -> str:
        """
        Extract text from PDF.

        Args:
            pdf_content: PDF bytes

        Returns:
            Extracted text
        """
        try:
            import pdfplumber
            import io

            with pdfplumber.open(io.BytesIO(pdf_content)) as pdf:
                text = ""
                for page in pdf.pages:
                    text += page.extract_text() + "\n"

            return text

        except:
            return "PDF text extraction failed"