devZenaight commited on
Commit
bebdbf4
·
1 Parent(s): f7368cd

Charts Bigger, Duplicate Headings

Browse files
app/DocumentGeneration/chart_generator.py CHANGED
@@ -89,7 +89,7 @@ def create_enhanced_chart_style(chart_type, data, title, business_idea):
89
  colors = ['#6A2EAB', '#5A2E9B', '#C0C0C0', '#FF8C00', '#FFD700', '#696969', '#FF4500']
90
 
91
  if chart_type == "DC": # Doughnut Chart
92
- fig, ax = plt.subplots(figsize=(8, 6))
93
 
94
  # Extract labels and values from data
95
  labels = [item[0] for item in data]
@@ -98,15 +98,16 @@ def create_enhanced_chart_style(chart_type, data, title, business_idea):
98
  # Create doughnut chart with enhanced styling
99
  wedges, texts, autotexts = ax.pie(values, labels=labels, colors=colors[:len(values)],
100
  autopct='%1.1f%%', startangle=90,
101
- wedgeprops=dict(width=0.6, edgecolor='white', linewidth=2))
 
102
 
103
  # Enhanced title styling
104
- ax.set_title(title, fontsize=16, fontweight='bold', pad=20, color='#374151')
105
 
106
  # Add legend with enhanced styling
107
  legend = ax.legend(wedges, labels, title="Categories",
108
  loc="center left", bbox_to_anchor=(1, 0, 0.5, 1),
109
- fontsize=10, title_fontsize=12)
110
  legend.get_title().set_color('#FF6B6B')
111
 
112
  # Add business name watermark
@@ -117,7 +118,7 @@ def create_enhanced_chart_style(chart_type, data, title, business_idea):
117
  return fig
118
 
119
  elif chart_type == "BG": # Bar Graph
120
- fig, ax = plt.subplots(figsize=(10, 6))
121
 
122
  # Extract labels and values
123
  labels = [item[0] for item in data]
@@ -131,15 +132,16 @@ def create_enhanced_chart_style(chart_type, data, title, business_idea):
131
  for bar, value in zip(bars, values):
132
  height = bar.get_height()
133
  ax.text(bar.get_x() + bar.get_width()/2., height + max(values)*0.01,
134
- f'{value}', ha='center', va='bottom', fontweight='bold')
135
 
136
  # Enhanced styling
137
- ax.set_title(title, fontsize=16, fontweight='bold', pad=20, color='#374151')
138
- ax.set_xlabel('Categories', fontsize=12, color='#6B7280')
139
- ax.set_ylabel('Values', fontsize=12, color='#6B7280')
140
 
141
  # Rotate x-axis labels for better readability
142
- plt.setp(ax.get_xticklabels(), rotation=45, ha='right')
 
143
 
144
  # Add grid for better readability
145
  ax.yaxis.grid(True, alpha=0.3)
@@ -153,7 +155,7 @@ def create_enhanced_chart_style(chart_type, data, title, business_idea):
153
  return fig
154
 
155
  elif chart_type == "LG": # Line Graph
156
- fig, ax = plt.subplots(figsize=(10, 6))
157
 
158
  # Extract data
159
  x_values = [item[0] for item in data]
@@ -167,14 +169,18 @@ def create_enhanced_chart_style(chart_type, data, title, business_idea):
167
  ax.fill_between(x_values, y_values, alpha=0.3, color='#4ECDC4')
168
 
169
  # Enhanced styling
170
- ax.set_title(title, fontsize=16, fontweight='bold', pad=20, color='#374151')
171
- ax.set_xlabel('Time Period', fontsize=12, color='#6B7280')
172
- ax.set_ylabel('Values', fontsize=12, color='#6B7280')
173
 
174
  # Add grid
175
  ax.grid(True, alpha=0.3)
176
  ax.set_axisbelow(True)
177
 
 
 
 
 
178
  # Add business name watermark
179
  fig.text(0.5, 0.02, f"Generated for: {business_idea}",
180
  ha='center', va='bottom', fontsize=8, color='#9CA3AF', style='italic')
@@ -183,7 +189,7 @@ def create_enhanced_chart_style(chart_type, data, title, business_idea):
183
  return fig
184
 
185
  elif chart_type == "PG": # Pie Graph
186
- fig, ax = plt.subplots(figsize=(8, 6))
187
 
188
  # Extract labels and values
189
  labels = [item[0] for item in data]
@@ -192,10 +198,11 @@ def create_enhanced_chart_style(chart_type, data, title, business_idea):
192
  # Create pie chart with enhanced styling
193
  wedges, texts, autotexts = ax.pie(values, labels=labels, colors=colors[:len(values)],
194
  autopct='%1.1f%%', startangle=90,
195
- wedgeprops=dict(edgecolor='white', linewidth=2))
 
196
 
197
  # Enhanced title styling
198
- ax.set_title(title, fontsize=16, fontweight='bold', pad=20, color='#374151')
199
 
200
  # Add business name watermark
201
  fig.text(0.5, 0.02, f"Generated for: {business_idea}",
@@ -221,7 +228,7 @@ def create_chart_from_data(chart_info: Dict[str, Any], business_idea: str) -> by
221
  if fig is None:
222
  # Fallback to original method if enhanced styling fails
223
  if chart_type == "DC": # Doughnut Chart
224
- fig, ax = plt.subplots(figsize=(6, 4.5)) # Smaller size for better page fit
225
 
226
  # Extract labels and values from data
227
  labels = [item[0] for item in data]
@@ -233,17 +240,18 @@ def create_chart_from_data(chart_info: Dict[str, Any], business_idea: str) -> by
233
  # Create doughnut chart
234
  wedges, texts, autotexts = ax.pie(values, labels=labels, colors=colors[:len(values)],
235
  autopct='%1.1f%%', startangle=90,
236
- wedgeprops=dict(width=0.6, edgecolor='white', linewidth=2))
 
237
 
238
  # Enhanced title
239
- ax.set_title(chart_title, fontsize=14, fontweight='bold', pad=15, color='#374151')
240
 
241
  # Add legend
242
- ax.legend(wedges, labels, title="Categories", loc="center left", bbox_to_anchor=(1, 0, 0.5, 1))
243
 
244
  plt.tight_layout()
245
  elif chart_type == "BG": # Bar Graph
246
- fig, ax = plt.subplots(figsize=(8, 5))
247
 
248
  # Extract labels and values
249
  labels = [item[0] for item in data]
@@ -260,14 +268,15 @@ def create_chart_from_data(chart_info: Dict[str, Any], business_idea: str) -> by
260
  for bar, value in zip(bars, values):
261
  height = bar.get_height()
262
  ax.text(bar.get_x() + bar.get_width()/2., height + max(values)*0.01,
263
- f'{value}', ha='center', va='bottom', fontweight='bold')
264
 
265
- ax.set_title(chart_title, fontsize=14, fontweight='bold', pad=15, color='#374151')
266
- ax.set_xlabel('Categories', fontsize=12, color='#6B7280')
267
- ax.set_ylabel('Values', fontsize=12, color='#6B7280')
268
 
269
  # Rotate x-axis labels
270
- plt.setp(ax.get_xticklabels(), rotation=45, ha='right')
 
271
 
272
  # Add grid
273
  ax.yaxis.grid(True, alpha=0.3)
@@ -275,7 +284,7 @@ def create_chart_from_data(chart_info: Dict[str, Any], business_idea: str) -> by
275
 
276
  plt.tight_layout()
277
  elif chart_type == "LG": # Line Graph
278
- fig, ax = plt.subplots(figsize=(8, 5))
279
 
280
  # Extract data
281
  x_values = [item[0] for item in data]
@@ -288,17 +297,21 @@ def create_chart_from_data(chart_info: Dict[str, Any], business_idea: str) -> by
288
  # Fill area under line
289
  ax.fill_between(x_values, y_values, alpha=0.3, color='#4ECDC4')
290
 
291
- ax.set_title(chart_title, fontsize=14, fontweight='bold', pad=15, color='#374151')
292
- ax.set_xlabel('Time Period', fontsize=12, color='#6B7280')
293
- ax.set_ylabel('Values', fontsize=12, color='#6B7280')
294
 
295
  # Add grid
296
  ax.grid(True, alpha=0.3)
297
  ax.set_axisbelow(True)
298
 
 
 
 
 
299
  plt.tight_layout()
300
  elif chart_type == "PG": # Pie Graph
301
- fig, ax = plt.subplots(figsize=(6, 4.5))
302
 
303
  # Extract labels and values
304
  labels = [item[0] for item in data]
@@ -310,9 +323,10 @@ def create_chart_from_data(chart_info: Dict[str, Any], business_idea: str) -> by
310
  # Create pie chart
311
  wedges, texts, autotexts = ax.pie(values, labels=labels, colors=colors[:len(values)],
312
  autopct='%1.1f%%', startangle=90,
313
- wedgeprops=dict(edgecolor='white', linewidth=2))
 
314
 
315
- ax.set_title(chart_title, fontsize=14, fontweight='bold', pad=15, color='#374151')
316
 
317
  plt.tight_layout()
318
  else:
 
89
  colors = ['#6A2EAB', '#5A2E9B', '#C0C0C0', '#FF8C00', '#FFD700', '#696969', '#FF4500']
90
 
91
  if chart_type == "DC": # Doughnut Chart
92
+ fig, ax = plt.subplots(figsize=(14, 10))
93
 
94
  # Extract labels and values from data
95
  labels = [item[0] for item in data]
 
98
  # Create doughnut chart with enhanced styling
99
  wedges, texts, autotexts = ax.pie(values, labels=labels, colors=colors[:len(values)],
100
  autopct='%1.1f%%', startangle=90,
101
+ wedgeprops=dict(width=0.6, edgecolor='white', linewidth=2),
102
+ textprops={'fontsize': 18})
103
 
104
  # Enhanced title styling
105
+ ax.set_title(title, fontsize=24, fontweight='bold', pad=20, color='#374151')
106
 
107
  # Add legend with enhanced styling
108
  legend = ax.legend(wedges, labels, title="Categories",
109
  loc="center left", bbox_to_anchor=(1, 0, 0.5, 1),
110
+ fontsize=16, title_fontsize=18)
111
  legend.get_title().set_color('#FF6B6B')
112
 
113
  # Add business name watermark
 
118
  return fig
119
 
120
  elif chart_type == "BG": # Bar Graph
121
+ fig, ax = plt.subplots(figsize=(16, 10))
122
 
123
  # Extract labels and values
124
  labels = [item[0] for item in data]
 
132
  for bar, value in zip(bars, values):
133
  height = bar.get_height()
134
  ax.text(bar.get_x() + bar.get_width()/2., height + max(values)*0.01,
135
+ f'{value}', ha='center', va='bottom', fontweight='bold', fontsize=18)
136
 
137
  # Enhanced styling
138
+ ax.set_title(title, fontsize=24, fontweight='bold', pad=20, color='#374151')
139
+ ax.set_xlabel('Categories', fontsize=18, color='#6B7280')
140
+ ax.set_ylabel('Values', fontsize=18, color='#6B7280')
141
 
142
  # Rotate x-axis labels for better readability
143
+ plt.setp(ax.get_xticklabels(), rotation=45, ha='right', fontsize=16)
144
+ plt.setp(ax.get_yticklabels(), fontsize=16)
145
 
146
  # Add grid for better readability
147
  ax.yaxis.grid(True, alpha=0.3)
 
155
  return fig
156
 
157
  elif chart_type == "LG": # Line Graph
158
+ fig, ax = plt.subplots(figsize=(16, 10))
159
 
160
  # Extract data
161
  x_values = [item[0] for item in data]
 
169
  ax.fill_between(x_values, y_values, alpha=0.3, color='#4ECDC4')
170
 
171
  # Enhanced styling
172
+ ax.set_title(title, fontsize=24, fontweight='bold', pad=20, color='#374151')
173
+ ax.set_xlabel('Time Period', fontsize=18, color='#6B7280')
174
+ ax.set_ylabel('Values', fontsize=18, color='#6B7280')
175
 
176
  # Add grid
177
  ax.grid(True, alpha=0.3)
178
  ax.set_axisbelow(True)
179
 
180
+ # Increase tick label font sizes
181
+ plt.setp(ax.get_xticklabels(), fontsize=16)
182
+ plt.setp(ax.get_yticklabels(), fontsize=16)
183
+
184
  # Add business name watermark
185
  fig.text(0.5, 0.02, f"Generated for: {business_idea}",
186
  ha='center', va='bottom', fontsize=8, color='#9CA3AF', style='italic')
 
189
  return fig
190
 
191
  elif chart_type == "PG": # Pie Graph
192
+ fig, ax = plt.subplots(figsize=(14, 10))
193
 
194
  # Extract labels and values
195
  labels = [item[0] for item in data]
 
198
  # Create pie chart with enhanced styling
199
  wedges, texts, autotexts = ax.pie(values, labels=labels, colors=colors[:len(values)],
200
  autopct='%1.1f%%', startangle=90,
201
+ wedgeprops=dict(edgecolor='white', linewidth=2),
202
+ textprops={'fontsize': 18})
203
 
204
  # Enhanced title styling
205
+ ax.set_title(title, fontsize=24, fontweight='bold', pad=20, color='#374151')
206
 
207
  # Add business name watermark
208
  fig.text(0.5, 0.02, f"Generated for: {business_idea}",
 
228
  if fig is None:
229
  # Fallback to original method if enhanced styling fails
230
  if chart_type == "DC": # Doughnut Chart
231
+ fig, ax = plt.subplots(figsize=(14, 10)) # Larger size for better visibility
232
 
233
  # Extract labels and values from data
234
  labels = [item[0] for item in data]
 
240
  # Create doughnut chart
241
  wedges, texts, autotexts = ax.pie(values, labels=labels, colors=colors[:len(values)],
242
  autopct='%1.1f%%', startangle=90,
243
+ wedgeprops=dict(width=0.6, edgecolor='white', linewidth=2),
244
+ textprops={'fontsize': 18})
245
 
246
  # Enhanced title
247
+ ax.set_title(chart_title, fontsize=22, fontweight='bold', pad=15, color='#374151')
248
 
249
  # Add legend
250
+ ax.legend(wedges, labels, title="Categories", loc="center left", bbox_to_anchor=(1, 0, 0.5, 1), fontsize=16)
251
 
252
  plt.tight_layout()
253
  elif chart_type == "BG": # Bar Graph
254
+ fig, ax = plt.subplots(figsize=(16, 10))
255
 
256
  # Extract labels and values
257
  labels = [item[0] for item in data]
 
268
  for bar, value in zip(bars, values):
269
  height = bar.get_height()
270
  ax.text(bar.get_x() + bar.get_width()/2., height + max(values)*0.01,
271
+ f'{value}', ha='center', va='bottom', fontweight='bold', fontsize=18)
272
 
273
+ ax.set_title(chart_title, fontsize=22, fontweight='bold', pad=15, color='#374151')
274
+ ax.set_xlabel('Categories', fontsize=18, color='#6B7280')
275
+ ax.set_ylabel('Values', fontsize=18, color='#6B7280')
276
 
277
  # Rotate x-axis labels
278
+ plt.setp(ax.get_xticklabels(), rotation=45, ha='right', fontsize=16)
279
+ plt.setp(ax.get_yticklabels(), fontsize=16)
280
 
281
  # Add grid
282
  ax.yaxis.grid(True, alpha=0.3)
 
284
 
285
  plt.tight_layout()
286
  elif chart_type == "LG": # Line Graph
287
+ fig, ax = plt.subplots(figsize=(16, 10))
288
 
289
  # Extract data
290
  x_values = [item[0] for item in data]
 
297
  # Fill area under line
298
  ax.fill_between(x_values, y_values, alpha=0.3, color='#4ECDC4')
299
 
300
+ ax.set_title(chart_title, fontsize=22, fontweight='bold', pad=15, color='#374151')
301
+ ax.set_xlabel('Time Period', fontsize=18, color='#6B7280')
302
+ ax.set_ylabel('Values', fontsize=18, color='#6B7280')
303
 
304
  # Add grid
305
  ax.grid(True, alpha=0.3)
306
  ax.set_axisbelow(True)
307
 
308
+ # Increase tick label font sizes
309
+ plt.setp(ax.get_xticklabels(), fontsize=16)
310
+ plt.setp(ax.get_yticklabels(), fontsize=16)
311
+
312
  plt.tight_layout()
313
  elif chart_type == "PG": # Pie Graph
314
+ fig, ax = plt.subplots(figsize=(14, 10))
315
 
316
  # Extract labels and values
317
  labels = [item[0] for item in data]
 
323
  # Create pie chart
324
  wedges, texts, autotexts = ax.pie(values, labels=labels, colors=colors[:len(values)],
325
  autopct='%1.1f%%', startangle=90,
326
+ wedgeprops=dict(edgecolor='white', linewidth=2),
327
+ textprops={'fontsize': 18})
328
 
329
+ ax.set_title(chart_title, fontsize=22, fontweight='bold', pad=15, color='#374151')
330
 
331
  plt.tight_layout()
332
  else:
app/DocumentGeneration/document_helpers.py CHANGED
@@ -113,17 +113,46 @@ def remove_duplicate_headings(content: str) -> str:
113
  continue
114
 
115
  # Check if this is any type of heading
 
 
 
 
116
  if line.startswith('#'):
117
- heading_text = re.sub(r'^#+\s*', '', line)
118
- # heading_text = re.sub(r'[*_`~]', '', heading_text).strip() # Commented out to preserve ** symbols
119
- heading_text = heading_text.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
- # Check for duplicates (case-insensitive)
122
- if heading_text.lower() in seen_headings:
123
- continue
 
 
 
 
 
124
 
125
- seen_headings.add(heading_text.lower())
126
- cleaned_lines.append(line)
 
 
 
 
 
 
 
127
  else:
128
  cleaned_lines.append(line)
129
 
 
113
  continue
114
 
115
  # Check if this is any type of heading
116
+ is_heading = False
117
+ heading_text = ""
118
+
119
+ # Markdown headings (# ## ### etc.)
120
  if line.startswith('#'):
121
+ heading_text = re.sub(r'^#+\s*', '', line).strip()
122
+ is_heading = True
123
+
124
+ # Bold headings (**text**)
125
+ elif line.startswith('**') and line.endswith('**') and len(line) > 4:
126
+ heading_text = line[2:-2].strip() # Remove ** symbols
127
+ is_heading = True
128
+
129
+ # Check for numbered headings (e.g., "8.1 Management Team")
130
+ elif re.match(r'^\d+\.?\d*\s+', line):
131
+ heading_text = re.sub(r'^\d+\.?\d*\s+', '', line).strip()
132
+ is_heading = True
133
+
134
+ if is_heading and heading_text:
135
+ # Normalize the heading text for comparison
136
+ normalized_text = heading_text.lower().strip()
137
 
138
+ # Also check for variations with "and" vs "&"
139
+ variations = [
140
+ normalized_text,
141
+ normalized_text.replace(' & ', ' and '),
142
+ normalized_text.replace(' and ', ' & '),
143
+ normalized_text.replace('&', 'and'),
144
+ normalized_text.replace('and', '&')
145
+ ]
146
 
147
+ # Check if any variation has been seen before
148
+ is_duplicate = any(var in seen_headings for var in variations)
149
+
150
+ if not is_duplicate:
151
+ # Add all variations to seen set
152
+ for var in variations:
153
+ seen_headings.add(var)
154
+ cleaned_lines.append(line)
155
+ # Skip duplicate headings
156
  else:
157
  cleaned_lines.append(line)
158
 
app/DocumentGeneration/pdf_generator.py CHANGED
@@ -161,7 +161,7 @@ def calculate_real_page_numbers(request: ExportRequest, filtered_sections):
161
 
162
  for chart_info in section_charts:
163
  try:
164
- chart_height_needed = 95 # Chart height (75) + title (20) + minimal spacing
165
  current_y = temp_pdf.get_y()
166
  charts_per_page += 1
167
 
@@ -191,7 +191,7 @@ def calculate_real_page_numbers(request: ExportRequest, filtered_sections):
191
  temp_pdf.line(25, y_title + 14, 25 + line_width, y_title + 14)
192
 
193
  # Add chart image
194
- img_width = 100
195
  x = (210 - img_width) / 2
196
  y_img = y_title + 20
197
 
@@ -201,7 +201,7 @@ def calculate_real_page_numbers(request: ExportRequest, filtered_sections):
201
  temp_img_path = tmp_img.name
202
 
203
  # Add chart
204
- chart_height = 75
205
  temp_pdf.image(temp_img_path, x=x, y=y_img, w=img_width)
206
  temp_pdf.ln(chart_height + 5)
207
 
@@ -455,24 +455,60 @@ def create_pdf_document(request: ExportRequest):
455
 
456
  # Remove the section title from content to prevent duplication (since we already added it above)
457
  section_title_clean = section.title.strip()
458
- print(f"DEBUG: Section title: '{section_title_clean}'")
459
- print(f"DEBUG: Original content before filtering: {content_text[:300]}...")
460
  content_lines = content_text.split('\n')
461
  filtered_lines = []
462
  for line in content_lines:
463
  line_clean = line.strip()
464
  # Skip lines that match the section title (with or without markdown)
465
  # Check for various markdown formats and variations
466
- if (line_clean == section_title_clean or
467
- line_clean == f"**{section_title_clean}**" or
468
- line_clean == f"# {section_title_clean}" or
469
- line_clean == f"## {section_title_clean}" or
470
- line_clean == f"### {section_title_clean}" or
471
- line_clean == f"#### {section_title_clean}" or
472
- # Also check if the line starts with the section title (for numbered headings)
473
- line_clean.startswith(f"{section_title_clean}") and len(line_clean) <= len(section_title_clean) + 10):
474
- continue
475
- filtered_lines.append(line)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  content_text = '\n'.join(filtered_lines)
477
 
478
  # Skip enhance_subheadings_with_bold for grouped rendering to preserve ** markers
@@ -549,7 +585,7 @@ def create_pdf_document(request: ExportRequest):
549
  for chart_info in section_charts:
550
  try:
551
  # Check if we need a new page for this chart
552
- chart_height_needed = 95 # Chart height (75) + title (20) + minimal spacing
553
 
554
  # Smart page break logic - allow multiple charts per page
555
  current_y = pdf.get_y()
@@ -571,8 +607,8 @@ def create_pdf_document(request: ExportRequest):
571
  # Subheader (title) and subtle rule
572
  y_title = add_subheader(pdf, chart_info["title"], 16, y_title)
573
 
574
- # Centered image with minimal border - optimized for more charts per page
575
- img_width = 100 # Optimized for smaller charts (6x4.5 ratio)
576
  x = (210 - img_width) / 2
577
  y_img = y_title
578
 
@@ -582,14 +618,14 @@ def create_pdf_document(request: ExportRequest):
582
  temp_img_path = tmp_img.name
583
 
584
  # Chart dimensions (no border needed)
585
- chart_height = 75 # Optimized height for 6:4.5 aspect ratio (100 * 4.5/6)
586
  chart_width = img_width
587
 
588
  # Image
589
  pdf.image(temp_img_path, x=x, y=y_img, w=chart_width)
590
 
591
  # Move below image and clean up (spacing based on actual chart height)
592
- pdf.ln(chart_height + 5) # Chart height (75) + minimal spacing between charts
593
  try:
594
  os.remove(temp_img_path)
595
  except Exception:
 
161
 
162
  for chart_info in section_charts:
163
  try:
164
+ chart_height_needed = 140 # Chart height (120) + title (20) + minimal spacing
165
  current_y = temp_pdf.get_y()
166
  charts_per_page += 1
167
 
 
191
  temp_pdf.line(25, y_title + 14, 25 + line_width, y_title + 14)
192
 
193
  # Add chart image
194
+ img_width = 160 # Much larger chart width
195
  x = (210 - img_width) / 2
196
  y_img = y_title + 20
197
 
 
201
  temp_img_path = tmp_img.name
202
 
203
  # Add chart
204
+ chart_height = 120 # Much larger chart height
205
  temp_pdf.image(temp_img_path, x=x, y=y_img, w=img_width)
206
  temp_pdf.ln(chart_height + 5)
207
 
 
455
 
456
  # Remove the section title from content to prevent duplication (since we already added it above)
457
  section_title_clean = section.title.strip()
 
 
458
  content_lines = content_text.split('\n')
459
  filtered_lines = []
460
  for line in content_lines:
461
  line_clean = line.strip()
462
  # Skip lines that match the section title (with or without markdown)
463
  # Check for various markdown formats and variations
464
+ should_skip = False
465
+
466
+ # Exact matches
467
+ if line_clean == section_title_clean:
468
+ should_skip = True
469
+
470
+ # Markdown heading variations
471
+ elif (line_clean == f"**{section_title_clean}**" or
472
+ line_clean == f"# {section_title_clean}" or
473
+ line_clean == f"## {section_title_clean}" or
474
+ line_clean == f"### {section_title_clean}" or
475
+ line_clean == f"#### {section_title_clean}" or
476
+ line_clean == f"##### {section_title_clean}" or
477
+ line_clean == f"###### {section_title_clean}"):
478
+ should_skip = True
479
+
480
+ # Numbered headings (e.g., "8.1 Management Team")
481
+ elif (line_clean.startswith(f"{section_title_clean}") and
482
+ len(line_clean) <= len(section_title_clean) + 15):
483
+ should_skip = True
484
+
485
+ # Check for headings that contain the section title with minimal additional text
486
+ elif (section_title_clean.lower() in line_clean.lower() and
487
+ len(line_clean) <= len(section_title_clean) + 20 and
488
+ (line_clean.startswith('#') or line_clean.startswith('**'))):
489
+ should_skip = True
490
+
491
+ # Check for variations with "and" vs "&" (e.g., "Management Team" vs "Management and Team")
492
+ title_variations = [
493
+ section_title_clean.replace(' & ', ' and '),
494
+ section_title_clean.replace(' and ', ' & '),
495
+ section_title_clean.replace('&', 'and'),
496
+ section_title_clean.replace('and', '&')
497
+ ]
498
+
499
+ for variation in title_variations:
500
+ if (line_clean == variation or
501
+ line_clean == f"**{variation}**" or
502
+ line_clean == f"# {variation}" or
503
+ line_clean == f"## {variation}" or
504
+ line_clean == f"### {variation}" or
505
+ line_clean == f"#### {variation}"):
506
+ should_skip = True
507
+ break
508
+
509
+ if not should_skip:
510
+ filtered_lines.append(line)
511
+
512
  content_text = '\n'.join(filtered_lines)
513
 
514
  # Skip enhance_subheadings_with_bold for grouped rendering to preserve ** markers
 
585
  for chart_info in section_charts:
586
  try:
587
  # Check if we need a new page for this chart
588
+ chart_height_needed = 140 # Chart height (120) + title (20) + minimal spacing
589
 
590
  # Smart page break logic - allow multiple charts per page
591
  current_y = pdf.get_y()
 
607
  # Subheader (title) and subtle rule
608
  y_title = add_subheader(pdf, chart_info["title"], 16, y_title)
609
 
610
+ # Centered image with larger dimensions for better visibility
611
+ img_width = 160 # Much larger chart width for better visibility
612
  x = (210 - img_width) / 2
613
  y_img = y_title
614
 
 
618
  temp_img_path = tmp_img.name
619
 
620
  # Chart dimensions (no border needed)
621
+ chart_height = 120 # Much larger height for better visibility
622
  chart_width = img_width
623
 
624
  # Image
625
  pdf.image(temp_img_path, x=x, y=y_img, w=chart_width)
626
 
627
  # Move below image and clean up (spacing based on actual chart height)
628
+ pdf.ln(chart_height + 5) # Chart height (120) + minimal spacing between charts
629
  try:
630
  os.remove(temp_img_path)
631
  except Exception: