66use PhpOffice \PhpSpreadsheet \Calculation \Exception as CalcExp ;
77use PhpOffice \PhpSpreadsheet \Calculation \Functions ;
88use PhpOffice \PhpSpreadsheet \Calculation \Information \ExcelError ;
9+ use PhpOffice \PhpSpreadsheet \Shared \StringHelper ;
910
1011class CharacterConvert
1112{
1213 use ArrayEnabled;
1314
15+ private static string $ oneByteCharacterSet = 'Windows-1252 ' ;
16+
1417 /**
1518 * CHAR.
1619 *
@@ -27,19 +30,45 @@ public static function character(mixed $character): array|string
2730 return self ::evaluateSingleArgumentArray ([self ::class, __FUNCTION__ ], $ character );
2831 }
2932
33+ return self ::characterBoth ($ character , true );
34+ }
35+
36+ /** @return array<mixed>|string */
37+ public static function characterUnicode (mixed $ character ): array |string
38+ {
39+ if (is_array ($ character )) {
40+ return self ::evaluateSingleArgumentArray ([self ::class, __FUNCTION__ ], $ character );
41+ }
42+
43+ return self ::characterBoth ($ character , false );
44+ }
45+
46+ private static function characterBoth (mixed $ character , bool $ ansi = true ): string
47+ {
3048 try {
3149 $ character = Helpers::validateInt ($ character , true );
3250 } catch (CalcExp $ e ) {
3351 return $ e ->getMessage ();
3452 }
3553
54+ if ($ ansi && $ character === 219 && self ::$ oneByteCharacterSet [0 ] === 'M ' ) {
55+ return '€ ' ;
56+ }
57+
3658 $ min = Functions::getCompatibilityMode () === Functions::COMPATIBILITY_OPENOFFICE ? 0 : 1 ;
37- if ($ character < $ min || $ character > 255 ) {
59+ if ($ character < $ min || ( $ ansi && $ character > 255 ) || $ character > 0x10FFFF ) {
3860 return ExcelError::VALUE ();
3961 }
40- $ result = iconv ('UCS-4LE ' , 'UTF-8 ' , pack ('V ' , $ character ));
62+ if ($ character > 0x10FFFD ) { // last assigned
63+ return ExcelError::NA ();
64+ }
65+ if ($ ansi ) {
66+ $ result = chr ($ character );
4167
42- return ($ result === false ) ? '' : $ result ;
68+ return (string ) iconv (self ::$ oneByteCharacterSet , 'UTF-8//IGNORE ' , $ result );
69+ }
70+
71+ return mb_chr ($ character , 'UTF-8 ' );
4372 }
4473
4574 /**
@@ -57,7 +86,28 @@ public static function code(mixed $characters): array|string|int
5786 if (is_array ($ characters )) {
5887 return self ::evaluateSingleArgumentArray ([self ::class, __FUNCTION__ ], $ characters );
5988 }
89+ if (is_bool ($ characters ) && Functions::getCompatibilityMode () === Functions::COMPATIBILITY_OPENOFFICE ) {
90+ $ characters = $ characters ? '1 ' : '0 ' ;
91+ }
6092
93+ return self ::codeBoth (StringHelper::convertToString ($ characters , convertBool: true ), true );
94+ }
95+
96+ /** @return array<mixed>|int|string */
97+ public static function codeUnicode (mixed $ characters ): array |string |int
98+ {
99+ if (is_array ($ characters )) {
100+ return self ::evaluateSingleArgumentArray ([self ::class, __FUNCTION__ ], $ characters );
101+ }
102+ if (is_bool ($ characters ) && Functions::getCompatibilityMode () === Functions::COMPATIBILITY_OPENOFFICE ) {
103+ $ characters = $ characters ? '1 ' : '0 ' ;
104+ }
105+
106+ return self ::codeBoth (StringHelper::convertToString ($ characters , convertBool: true ), false );
107+ }
108+
109+ private static function codeBoth (string $ characters , bool $ ansi = true ): int |string
110+ {
61111 try {
62112 $ characters = Helpers::extractString ($ characters , true );
63113 } catch (CalcExp $ e ) {
@@ -72,22 +122,27 @@ public static function code(mixed $characters): array|string|int
72122 if (mb_strlen ($ characters , 'UTF-8 ' ) > 1 ) {
73123 $ character = mb_substr ($ characters , 0 , 1 , 'UTF-8 ' );
74124 }
125+ if ($ ansi && $ character === '€ ' && self ::$ oneByteCharacterSet [0 ] === 'M ' ) {
126+ return 219 ;
127+ }
128+
129+ $ result = mb_ord ($ character , 'UTF-8 ' );
130+ if ($ ansi ) {
131+ $ result = iconv ('UTF-8 ' , self ::$ oneByteCharacterSet . '//IGNORE ' , $ character );
132+
133+ return ($ result !== '' ) ? ord ("$ result " ) : 63 ; // question mark
134+ }
75135
76- return self :: unicodeToOrd ( $ character ) ;
136+ return $ result ;
77137 }
78138
79- private static function unicodeToOrd ( string $ character ): int
139+ public static function setWindowsCharacterSet ( ): void
80140 {
81- $ retVal = 0 ;
82- $ iconv = iconv ('UTF-8 ' , 'UCS-4LE ' , $ character );
83- if ($ iconv !== false ) {
84- /** @var false|int[] */
85- $ result = unpack ('V ' , $ iconv );
86- if (is_array ($ result ) && isset ($ result [1 ])) {
87- $ retVal = $ result [1 ];
88- }
89- }
141+ self ::$ oneByteCharacterSet = 'Windows-1252 ' ;
142+ }
90143
91- return $ retVal ;
144+ public static function setMacCharacterSet (): void
145+ {
146+ self ::$ oneByteCharacterSet = 'MAC ' ;
92147 }
93148}
0 commit comments