Skip to content

Commit b94aa25

Browse files
authored
Fix fatal error in PDF reports when PHP iconv extension is missing (#8126)
Adds convertToLatin1() helper in ChurchInfoReport with mb_convert_encoding fallback for servers missing the iconv PHP extension. Replaces 13 direct iconv() call sites across ChurchInfoReport, PdfDirectory, PdfLabel, and NameTags. Fixes #8124 Note: This is a short-term fix. Issue #8136 tracks the long-term migration from FPDF to mPDF, which eliminates the encoding layer entirely.
1 parent 6088233 commit b94aa25

File tree

5 files changed

+91
-14
lines changed

5 files changed

+91
-14
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/// <reference types="cypress" />
2+
3+
/**
4+
* Tests for GitHub Issue: Report to PDF error - iconv extension missing
5+
*
6+
* When the PHP `iconv` extension is not installed, PDF report generation
7+
* (Church Directory, Tax Statements, Name Tags) crashed with a fatal
8+
* "Call to undefined function ChurchCRM\Reports\iconv()" error.
9+
*
10+
* The fix adds a convertToLatin1() helper in ChurchInfoReport that falls
11+
* back to mb_convert_encoding() when iconv is unavailable.
12+
*
13+
* These tests verify that the affected report pages load and generate
14+
* output without fatal PHP errors.
15+
*/
16+
describe("PDF Reports - iconv fallback fix", () => {
17+
beforeEach(() => {
18+
cy.setupAdminSession();
19+
});
20+
21+
it("Church Directory report page loads without fatal error", () => {
22+
cy.visit("DirectoryReports.php");
23+
cy.contains("Directory reports");
24+
cy.get("body").should("not.contain", "Fatal error");
25+
cy.get("body").should("not.contain", "Call to undefined function");
26+
});
27+
28+
it("Tax Statement (Giving Report) PDF generation does not return a server error", () => {
29+
cy.visit("FinancialReports.php");
30+
cy.contains("Financial Reports");
31+
32+
cy.get("#FinancialReportTypes").select("Giving Report");
33+
cy.get("#FinancialReports").submit();
34+
cy.contains("Financial Reports: Giving Report");
35+
36+
// Set a broad date range to include demo data
37+
cy.get("#DateStart").clear().type("2005-01-01");
38+
cy.get("#DateEnd").clear().type("2025-12-31");
39+
40+
cy.get('input[name="output"][value="pdf"]').check();
41+
42+
// Use the same intercept pattern as tax-report-pdf.spec.js which is known to work
43+
cy.intercept("POST", "**/Reports/TaxReport.php").as("taxReport");
44+
cy.get("#createReport").click();
45+
46+
cy.wait("@taxReport").then((interception) => {
47+
expect(interception.response.statusCode).to.not.equal(500);
48+
49+
const body = interception.response.body || "";
50+
expect(body).to.not.include("Call to undefined function");
51+
expect(body).to.not.include("Fatal error");
52+
});
53+
});
54+
55+
it("Name Tags / Labels page loads without fatal error", () => {
56+
cy.visit("LettersAndLabels.php");
57+
cy.contains("Letters and Mailing Labels");
58+
cy.get("body").should("not.contain", "Fatal error");
59+
cy.get("body").should("not.contain", "Call to undefined function");
60+
});
61+
});

src/ChurchCRM/Reports/ChurchInfoReport.php

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,25 @@ class ChurchInfoReport extends FPDF
2121

2222
public string $paperFormat = 'Letter';
2323

24+
/**
25+
* Converts a UTF-8 string to ISO-8859-1 for FPDF compatibility.
26+
* Uses iconv() if available, falls back to mb_convert_encoding().
27+
*/
28+
public static function convertToLatin1(string $str): string
29+
{
30+
if (function_exists('iconv')) {
31+
$result = iconv('UTF-8', 'ISO-8859-1//TRANSLIT//IGNORE', $str);
32+
if ($result !== false) {
33+
return $result;
34+
}
35+
}
36+
$result = mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
37+
return is_string($result) ? $result : $str;
38+
}
39+
2440
public function printRightJustified($x, $y, $str): void
2541
{
26-
$strconv = iconv('UTF-8', 'ISO-8859-1', $str);
42+
$strconv = self::convertToLatin1($str);
2743
$iLen = strlen($strconv);
2844
$nMoveBy = 10 - 2 * $iLen;
2945
$this->SetXY($x + $nMoveBy, $y);
@@ -32,28 +48,28 @@ public function printRightJustified($x, $y, $str): void
3248

3349
public function printRightJustifiedCell($x, $y, $wid, $str): void
3450
{
35-
$strconv = iconv('UTF-8', 'ISO-8859-1', $str);
51+
$strconv = self::convertToLatin1($str);
3652
$this->SetXY($x, $y);
3753
$this->Cell($wid, SystemConfig::getValue('incrementY'), $strconv, 1, 0, 'R');
3854
}
3955

4056
public function printCenteredCell($x, $y, $wid, $str): void
4157
{
42-
$strconv = iconv('UTF-8', 'ISO-8859-1', $str);
58+
$strconv = self::convertToLatin1($str);
4359
$this->SetXY($x, $y);
4460
$this->Cell($wid, SystemConfig::getValue('incrementY'), $strconv, 1, 0, 'C');
4561
}
4662

4763
public function writeAt($x, $y, $str): void
4864
{
49-
$strconv = iconv('UTF-8', 'ISO-8859-1', $str);
65+
$strconv = self::convertToLatin1($str);
5066
$this->SetXY($x, $y);
5167
$this->Write(SystemConfig::getValue('incrementY'), $strconv);
5268
}
5369

5470
public function writeAtCell($x, $y, $wid, $str): void
5571
{
56-
$strconv = iconv('UTF-8', 'ISO-8859-1', $str);
72+
$strconv = self::convertToLatin1($str);
5773
$this->SetXY($x, $y);
5874
$this->MultiCell($wid, 4, $strconv, 1);
5975
}

src/ChurchCRM/Reports/PdfDirectory.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ public function __construct($nc = 1, $paper = 'letter', $fs = 10, $ls = 4)
4343
$this->_NCols = $nc;
4444
$this->_ColWidth = 190 / $nc - $this->_Gutter;
4545

46-
$this->sChurchNameEncoded = iconv('UTF-8', 'ISO-8859-1', SystemConfig::getValue('sChurchName'));
47-
$this->sChurchAddressEncoded = iconv('UTF-8', 'ISO-8859-1', SystemConfig::getValue('sChurchAddress'));
48-
$this->sChurchCityEncoded = iconv('UTF-8', 'ISO-8859-1', SystemConfig::getValue('sChurchCity'));
49-
$this->sChurchStateEncoded = iconv('UTF-8', 'ISO-8859-1', SystemConfig::getValue('sChurchState'));
46+
$this->sChurchNameEncoded = self::convertToLatin1(SystemConfig::getValue('sChurchName'));
47+
$this->sChurchAddressEncoded = self::convertToLatin1(SystemConfig::getValue('sChurchAddress'));
48+
$this->sChurchCityEncoded = self::convertToLatin1(SystemConfig::getValue('sChurchCity'));
49+
$this->sChurchStateEncoded = self::convertToLatin1(SystemConfig::getValue('sChurchState'));
5050
}
5151

5252
public function header(): void
@@ -125,7 +125,7 @@ public function titlePage(): void
125125
);
126126
$this->MultiCell(197, 10, $sContact, 0, 'C');
127127
$this->Cell(10);
128-
$sDirectoryDisclaimer = iconv('UTF-8', 'ISO-8859-1', $sDirectoryDisclaimer);
128+
$sDirectoryDisclaimer = self::convertToLatin1($sDirectoryDisclaimer);
129129
$this->MultiCell(197, 10, $sDirectoryDisclaimer, 0, 'C');
130130
$this->addPage();
131131
}
@@ -489,7 +489,7 @@ public function addRecord($sName, $text, $numlines, $fid, $pid): void
489489
}
490490
$this->checkLines($numlines, $dirimg);
491491

492-
$this->printName(iconv('UTF-8', 'ISO-8859-1', $sName));
492+
$this->printName(self::convertToLatin1($sName));
493493

494494
$_PosX = ($this->_Column * ($this->_ColWidth + $this->_Gutter)) + $this->_Margin_Left;
495495
$_PosY = $this->GetY();
@@ -503,7 +503,7 @@ public function addRecord($sName, $text, $numlines, $fid, $pid): void
503503
$this->SetXY($_PosX, $_PosY + $h + 2);
504504
}
505505

506-
$this->MultiCell($this->_ColWidth, $this->_LS, iconv('UTF-8', 'ISO-8859-1', $text), 0, 'L');
506+
$this->MultiCell($this->_ColWidth, $this->_LS, self::convertToLatin1($text), 0, 'L');
507507
$this->SetY($this->GetY() + $this->_LS);
508508
}
509509
}

src/ChurchCRM/Reports/PdfLabel.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ public function addPdfLabel($texte): void
169169
$_PosX = $this->_Margin_Left + ($this->_COUNTX * ($this->_Width + $this->_X_Space));
170170
$_PosY = $this->_Margin_Top + ($this->_COUNTY * ($this->_Height + $this->_Y_Space));
171171
$this->SetXY($_PosX + 3, $_PosY + 3);
172-
$this->MultiCell($this->_Width, $this->_Line_Height, iconv('UTF-8', 'ISO-8859-1', $texte));
172+
$this->MultiCell($this->_Width, $this->_Line_Height, self::convertToLatin1($texte));
173173
$this->_COUNTY++;
174174

175175
if ($this->_COUNTY == $this->_Y_Number) {

src/Reports/NameTags.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
}
5757

5858
$pdf->SetXY($PosX + $pdf->_Width / 2, $PosY + 3);
59-
$pdf->MultiCell($pdf->_Width / 2, $pdf->_Line_Height, iconv('UTF-8', 'ISO-8859-1', $labelStr));
59+
$pdf->MultiCell($pdf->_Width / 2, $pdf->_Line_Height, $pdf->convertToLatin1($labelStr));
6060
$pdf->setCharSize($sFontSize);
6161
$pdf->addPdfLabel('');
6262
} else {

0 commit comments

Comments
 (0)