| from fpdf import FPDF |
| import re |
|
|
|
|
| class PDF(FPDF): |
| def footer(self): |
| """Footer with custom text on every page""" |
| self.set_y(-15) |
| self.set_font('Arial', 'I', 8) |
| self.set_text_color(128, 128, 128) |
| self.cell(0, 10, 'Generated by Zouq-ul-ilm', 0, 0, 'C') |
|
|
|
|
| def pdf1(text): |
| pdf = PDF('P', 'mm', 'A4') |
| pdf.add_page() |
| pdf.set_auto_page_break(auto=True, margin=15) |
| |
| |
| h1_pattern = re.compile(r'^#\s+(.+)', re.MULTILINE) |
| h2_pattern = re.compile(r'^##\s+(.+)', re.MULTILINE) |
| h3_pattern = re.compile(r'^###\s+(.+)', re.MULTILINE) |
| bullet_pattern = re.compile(r'^\s*[-*]\s+(.+)', re.MULTILINE) |
| |
| |
| lines = text.split('\n') |
| |
| for line in lines: |
| line = line.strip() |
| |
| if not line: |
| pdf.ln(4) |
| continue |
| |
| |
| if h1_pattern.match(line): |
| pdf.ln(4) |
| pdf.set_font('Arial', 'B', 18) |
| pdf.set_text_color(0, 0, 0) |
| clean_text = re.sub(r'^#\s+', '', line) |
| pdf.multi_cell(0, 10, clean_text.encode('latin-1', 'ignore').decode('latin-1')) |
| pdf.ln(3) |
| |
| elif h2_pattern.match(line): |
| pdf.ln(3) |
| pdf.set_font('Arial', 'B', 15) |
| pdf.set_text_color(0, 0, 0) |
| clean_text = re.sub(r'^##\s+', '', line) |
| pdf.multi_cell(0, 8, clean_text.encode('latin-1', 'ignore').decode('latin-1')) |
| pdf.ln(2) |
| |
| elif h3_pattern.match(line): |
| pdf.ln(2) |
| pdf.set_font('Arial', 'B', 13) |
| pdf.set_text_color(0, 0, 0) |
| clean_text = re.sub(r'^###\s+', '', line) |
| pdf.multi_cell(0, 7, clean_text.encode('latin-1', 'ignore').decode('latin-1')) |
| pdf.ln(2) |
| |
| elif bullet_pattern.match(line): |
| |
| clean_text = re.sub(r'^\s*[-*]\s+', '', line) |
| process_bullet_point(pdf, clean_text) |
| |
| else: |
| |
| pdf.set_font('Arial', '', 11) |
| pdf.set_text_color(0, 0, 0) |
| process_inline_formatting(pdf, line) |
| pdf.ln(5) |
| |
| pdf.output(f"notes.pdf") |
|
|
|
|
| def process_bullet_point(pdf, text): |
| """Process bullet points with proper wrapping""" |
| left_margin = pdf.l_margin |
| bullet_indent = 5 |
| text_indent = 12 |
| |
| |
| pdf.set_x(left_margin + bullet_indent) |
| pdf.set_font('Arial', '', 11) |
| pdf.cell(5, 5, chr(149), 0, 0) |
| |
| |
| process_inline_formatting_wrapped(pdf, text, left_margin + text_indent) |
| pdf.ln(5) |
|
|
|
|
| def process_inline_formatting_wrapped(pdf, text, left_indent): |
| """Process inline formatting with proper text wrapping""" |
| |
| segments = re.split(r'(\*\*.*?\*\*|__.*?__|_.*?_|~~.*?~~)', text) |
| |
| |
| pdf.set_x(left_indent) |
| |
| |
| right_margin = pdf.w - pdf.r_margin |
| |
| for segment in segments: |
| if not segment: |
| continue |
| |
| |
| if re.match(r'\*\*.*?\*\*|__.*?__', segment): |
| style = 'B' |
| clean_text = re.sub(r'\*\*|__', '', segment) |
| elif re.match(r'_.*?_|~~.*?~~', segment): |
| style = 'U' |
| clean_text = re.sub(r'_+|~+', '', segment) |
| else: |
| style = '' |
| clean_text = segment |
| |
| |
| try: |
| safe_text = clean_text.encode('latin-1', 'ignore').decode('latin-1') |
| except: |
| safe_text = clean_text.encode('ascii', 'ignore').decode('ascii') |
| |
| |
| words = safe_text.split(' ') |
| |
| for i, word in enumerate(words): |
| |
| if i > 0 or segment != segments[0]: |
| test_word = ' ' + word |
| else: |
| test_word = word |
| |
| pdf.set_font('Arial', style, 11) |
| word_width = pdf.get_string_width(test_word) |
| |
| |
| current_x = pdf.get_x() |
| |
| |
| if current_x + word_width > right_margin: |
| |
| pdf.ln(5) |
| pdf.set_x(left_indent) |
| |
| test_word = word |
| word_width = pdf.get_string_width(test_word) |
| |
| |
| pdf.cell(word_width, 5, test_word, 0, 0) |
|
|
|
|
| def process_inline_formatting(pdf, text): |
| """Process inline formatting for normal paragraphs""" |
| left_margin = pdf.l_margin |
| process_inline_formatting_wrapped(pdf, text, left_margin) |
|
|
|
|
| |
| if __name__ == "__main__": |
| sample_text = """# Hashing: Exam Prep Notes |
| |
| Hashing is a fundamental technique in computer science used for **efficient data storage and retrieval**. It allows us to quickly find an element in a collection (like an array or a list) without having to search through the whole thing. |
| |
| ## 1. Core Concepts: |
| |
| * **Hash Table:** A data structure that implements an associative array, a structure that can map keys to values (like a dictionary). |
| * **Key:** The input value we want to store or retrieve. The key is unique and identifies the data. |
| * **Value:** The actual data associated with a key. The value is what we want to store in the hash table. |
| * **Hash Function (h(x)):** A function that takes a key as input and returns an index (usually an integer number) where the corresponding value should be stored in the hash table. This index is also called the **hash value** or **hash code**. |
| * **Hash Table Size (M):** The total number of slots or buckets available in the hash table that are allocated for storage. |
| * **Collision:** When two different keys produce the same hash value and map to the same slot (or bucket or slot). This is inevitable and needs to be handled. |
| * **Load Factor (λ):** A measure of how full the hash table is, calculated as λ = n/M where n is the number of elements and M is the hash table size. A high load factor increases the likelihood of collisions. |
| |
| ## 2. How Hashing Works: |
| |
| 1. **Key is provided.** |
| 2. **Hash Function is applied to the key:** The hash function processes the key and generates an index value. |
| 3. **The value is stored (or retrieved) at the calculated index** in the hash table. If there's a collision (the index is already occupied), a collision resolution technique is used. |
| |
| ## 3. Hash Functions - Key Considerations: |
| |
| * **Deterministic:** The same key should always produce the same hash value. This ensures consistency. |
| * **Uniformity:** Ideally, the hash function should distribute keys uniformly across the hash table to minimize collisions. |
| * **Efficiency:** The hash function should be fast to compute, as it's called frequently during insertions, deletions, and lookups. |
| |
| ## 4. Common Hash Functions: |
| |
| * **Division Method:** h(k) = k mod M. Simple but can lead to clustering if M is not chosen carefully. |
| * **Multiplication Method:** h(k) = floor(M * (kA mod 1)) where A is a constant (often 0.618034). More complex but generally better distribution. |
| * **Mid-Square Method:** Square the key, extract the middle digits, and use them as the hash value. |
| |
| This is a comprehensive overview of hashing concepts for your exam preparation.""" |
|
|
| pdf1(sample_text) |