Przeglądaj źródła

Enhance PDF report generation with improved color schemes, standardized formatting, and better error handling in fpdf2 integration

lechibang-1512 2 miesięcy temu
rodzic
commit
5b111ee825
1 zmienionych plików z 120 dodań i 12 usunięć
  1. 120
    12
      gitstats.py

+ 120
- 12
gitstats.py Wyświetl plik

@@ -3249,16 +3249,106 @@ class PDFReportCreator(ReportCreator):
3249 3249
 		ReportCreator.__init__(self)
3250 3250
 		self.pdf = None
3251 3251
 		self.output_path = None
3252
+		# Define color schemes for better visual appeal
3253
+		self.colors = {
3254
+			'header': (41, 128, 185),    # Blue
3255
+			'text': (0, 0, 0),           # Black
3256
+			'table_header': (52, 152, 219), # Light blue
3257
+			'table_alt': (245, 245, 245)    # Light gray
3258
+		}
3259
+	
3260
+	def _set_color(self, color_type='text', fill=False):
3261
+		"""Set text or fill color using predefined color scheme."""
3262
+		if color_type in self.colors:
3263
+			r, g, b = self.colors[color_type]
3264
+			if fill:
3265
+				self.pdf.set_fill_color(r, g, b)
3266
+			else:
3267
+				self.pdf.set_text_color(r, g, b)
3268
+	
3269
+	def _add_section_header(self, title, level=1):
3270
+		"""Add a standardized section header with consistent formatting."""
3271
+		# Add some space before header
3272
+		self.pdf.ln(h=10)
3273
+		
3274
+		# Set header color and font
3275
+		self._set_color('header')
3276
+		if level == 1:
3277
+			self.pdf.set_font('helvetica', 'B', 20)
3278
+			height = 15
3279
+		elif level == 2:
3280
+			self.pdf.set_font('helvetica', 'B', 16)
3281
+			height = 12
3282
+		else:
3283
+			self.pdf.set_font('helvetica', 'B', 14)
3284
+			height = 10
3285
+		
3286
+		# Add the header
3287
+		self.pdf.cell(0, height, title, 0, new_x=XPos.LMARGIN, new_y=YPos.NEXT, align='L')
3288
+		
3289
+		# Reset color to text
3290
+		self._set_color('text')
3291
+		self.pdf.ln(h=5)  # Small gap after header
3292
+	
3293
+	def _create_table_header(self, headers, widths=None, font_size=9):
3294
+		"""Create a standardized table header with consistent formatting."""
3295
+		if widths is None:
3296
+			# Auto-calculate widths if not provided
3297
+			total_width = 180  # Reasonable default
3298
+			widths = [total_width // len(headers)] * len(headers)
3299
+		
3300
+		# Set header styling
3301
+		self._set_color('table_header')
3302
+		self._set_color('table_header', fill=True)
3303
+		self.pdf.set_font('helvetica', 'B', font_size)
3304
+		
3305
+		# Create header cells
3306
+		for i, (header, width) in enumerate(zip(headers, widths)):
3307
+			is_last = (i == len(headers) - 1)
3308
+			new_x = XPos.LMARGIN if is_last else XPos.RIGHT
3309
+			new_y = YPos.NEXT if is_last else YPos.TOP
3310
+			
3311
+			self.pdf.cell(width, 8, str(header), 1, 
3312
+						 new_x=new_x, new_y=new_y, align='C', fill=True)
3313
+		
3314
+		# Reset styling for table content
3315
+		self._set_color('text')
3316
+		self.pdf.set_font('helvetica', '', font_size - 1)
3317
+	
3318
+	def _create_table_row(self, values, widths, alternate_row=False, font_size=8):
3319
+		"""Create a table row with optional alternating background."""
3320
+		if alternate_row:
3321
+			self._set_color('table_alt', fill=True)
3322
+		
3323
+		for i, (value, width) in enumerate(zip(values, widths)):
3324
+			is_last = (i == len(values) - 1)
3325
+			new_x = XPos.LMARGIN if is_last else XPos.RIGHT
3326
+			new_y = YPos.NEXT if is_last else YPos.TOP
3327
+			
3328
+			# Truncate long values to fit
3329
+			str_value = str(value)
3330
+			if len(str_value) > width // 3:  # Rough character width estimation
3331
+				str_value = str_value[:width//3-2] + '...'
3332
+			
3333
+			self.pdf.cell(width, 6, str_value, 1,
3334
+						 new_x=new_x, new_y=new_y, align='C', fill=alternate_row)
3252 3335
 	
3253 3336
 	def create(self, data, path):
3254 3337
 		ReportCreator.create(self, data, path)
3255 3338
 		self.title = data.projectname
3256 3339
 		self.output_path = path
3257 3340
 		
3258
-		# Initialize PDF document
3341
+		# Initialize PDF document with fpdf2 features
3259 3342
 		self.pdf = FPDF()
3260 3343
 		self.pdf.set_auto_page_break(auto=True, margin=15)
3261 3344
 		
3345
+		# Set metadata for better PDF properties
3346
+		self.pdf.set_title(f"GitStats Report - {data.projectname}")
3347
+		self.pdf.set_author("GitStats")
3348
+		self.pdf.set_subject(f"Git repository analysis for {data.projectname}")
3349
+		self.pdf.set_creator("GitStats with fpdf2")
3350
+		self.pdf.set_keywords("git,statistics,analysis,repository")
3351
+		
3262 3352
 		# Create all pages (tabs)
3263 3353
 		self._create_title_page(data)
3264 3354
 		self._create_general_page(data)
@@ -3270,13 +3360,24 @@ class PDFReportCreator(ReportCreator):
3270 3360
 		self._create_tags_page(data)
3271 3361
 		self._create_branches_page(data)
3272 3362
 		
3273
-		# Save PDF
3363
+		# Save PDF with fpdf2's enhanced output method
3274 3364
 		pdf_path = os.path.join(path, f"gitstats_{data.projectname.replace(' ', '_')}.pdf")
3275
-		self.pdf.output(pdf_path)
3276
-		print(f"PDF report saved to: {pdf_path}")
3365
+		
3366
+		# Use fpdf2's output method with proper file handling
3367
+		try:
3368
+			self.pdf.output(pdf_path)
3369
+			print(f"PDF report saved to: {pdf_path}")
3370
+			# Verify file was created and has content
3371
+			if os.path.exists(pdf_path) and os.path.getsize(pdf_path) > 0:
3372
+				print(f"PDF file size: {os.path.getsize(pdf_path)} bytes")
3373
+			else:
3374
+				print("Warning: PDF file was not created properly")
3375
+		except Exception as e:
3376
+			print(f"Error saving PDF: {e}")
3377
+			raise
3277 3378
 	
3278 3379
 	def _add_chart_if_exists(self, chart_filename, width=None, height=None):
3279
-		"""Add a chart image to the PDF if it exists."""
3380
+		"""Add a chart image to the PDF if it exists, with improved fpdf2 handling."""
3280 3381
 		chart_path = os.path.join(self.output_path, chart_filename)
3281 3382
 		if os.path.exists(chart_path):
3282 3383
 			try:
@@ -3284,22 +3385,29 @@ class PDFReportCreator(ReportCreator):
3284 3385
 				x = self.pdf.get_x()
3285 3386
 				y = self.pdf.get_y()
3286 3387
 				
3287
-				# Calculate dimensions
3388
+				# Calculate dimensions with better defaults
3288 3389
 				if width is None:
3289 3390
 					width = 150  # Default width
3290 3391
 				if height is None:
3291 3392
 					height = 80  # Default height
3292 3393
 				
3293
-				# Check if there's enough space, if not add page break
3294
-				if y + height > 280:  # 280 is roughly page height minus margin
3394
+				# Get page dimensions for better space calculation
3395
+				page_width = self.pdf.w
3396
+				page_height = self.pdf.h
3397
+				margin = 15  # Same as auto_page_break margin
3398
+				
3399
+				# Check if there's enough space on current page
3400
+				if y + height > (page_height - margin):
3295 3401
 					self.pdf.add_page()
3402
+					x = self.pdf.get_x()
3296 3403
 					y = self.pdf.get_y()
3297 3404
 				
3298
-				# Add image
3299
-				self.pdf.image(chart_path, x, y, width, height)
3405
+				# Add image with fpdf2's enhanced image handling
3406
+				# fpdf2 automatically handles different image formats
3407
+				self.pdf.image(chart_path, x=x, y=y, w=width, h=height)
3300 3408
 				
3301
-				# Move cursor below image
3302
-				self.pdf.set_y(y + height + 5)
3409
+				# Move cursor below image with better spacing
3410
+				self.pdf.set_y(y + height + 8)  # Increased spacing for better layout
3303 3411
 				
3304 3412
 				return True
3305 3413
 			except Exception as e: