number_formatter.cpp 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097
  1. // Copyright (c) 2014-2021 Thomas Fussell
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE
  20. //
  21. // @license: http://www.opensource.org/licenses/mit-license.php
  22. // @author: see AUTHORS file
  23. #include <algorithm>
  24. #include <cctype>
  25. #include <cmath>
  26. #include <limits>
  27. #include <xlnt/utils/exceptions.hpp>
  28. #include <xlnt/utils/numeric.hpp>
  29. #include <detail/default_case.hpp>
  30. #include <detail/number_format/number_formatter.hpp>
  31. namespace {
  32. const std::unordered_map<int, std::string> known_locales()
  33. {
  34. static const std::unordered_map<int, std::string> all = std::unordered_map<int, std::string>(
  35. {
  36. {0x1, "Arabic"},
  37. {0x2, "Bulgarian"},
  38. {0x3, "Catalan"},
  39. {0x4, "Chinese (Simplified)"},
  40. {0x4, "Chinese (Simplified) Legacy"},
  41. {0x5, "Czech"},
  42. {0x6, "Danish"},
  43. {0x7, "German"},
  44. {0x8, "Greek"},
  45. {0x9, "English"},
  46. {0xA, "Spanish"},
  47. {0xB, "Finnish"},
  48. {0xC, "French"},
  49. {0xD, "Hebrew"},
  50. {0xE, "Hungarian"},
  51. {0xF, "Icelandic"},
  52. {0x10, "Italian"},
  53. {0x11, "Japanese"},
  54. {0x12, "Korean"},
  55. {0x13, "Dutch"},
  56. {0x14, "Norwegian"},
  57. {0x15, "Polish"},
  58. {0x16, "Portuguese"},
  59. {0x17, "Romansh"},
  60. {0x18, "Romanian"},
  61. {0x19, "Russian"},
  62. {0x1A, "Croatian"},
  63. {0x1B, "Slovak"},
  64. {0x1C, "Albanian"},
  65. {0x1D, "Swedish"},
  66. {0x1E, "Thai"},
  67. {0x1F, "Turkish"},
  68. {0x20, "Urdu"},
  69. {0x21, "Indonesian"},
  70. {0x22, "Ukrainian"},
  71. {0x23, "Belarusian"},
  72. {0x24, "Slovenian"},
  73. {0x25, "Estonian"},
  74. {0x26, "Latvian"},
  75. {0x27, "Lithuanian"},
  76. {0x28, "Tajik"},
  77. {0x29, "Persian"},
  78. {0x2A, "Vietnamese"},
  79. {0x2B, "Armenian"},
  80. {0x2C, "Azerbaijani"},
  81. {0x2D, "Basque"},
  82. {0x2E, "Upper Sorbian"},
  83. {0x2F, "Macedonian (FYROM)"},
  84. {0x30, "Southern Sotho"},
  85. {0x31, "Tsonga"},
  86. {0x32, "Setswana"},
  87. {0x33, "Venda"},
  88. {0x34, "isiXhosa"},
  89. {0x35, "isiZulu"},
  90. {0x36, "Afrikaans"},
  91. {0x37, "Georgian"},
  92. {0x38, "Faroese"},
  93. {0x39, "Hindi"},
  94. {0x3A, "Maltese"},
  95. {0x3B, "Sami (Northern)"},
  96. {0x3C, "Irish"},
  97. {0x3D, "Yiddish"},
  98. {0x3E, "Malay"},
  99. {0x3F, "Kazakh"},
  100. {0x40, "Kyrgyz"},
  101. {0x41, "Kiswahili"},
  102. {0x42, "Turkmen"},
  103. {0x43, "Uzbek"},
  104. {0x44, "Tatar"},
  105. {0x45, "Bangla"},
  106. {0x46, "Punjabi"},
  107. {0x47, "Gujarati"},
  108. {0x48, "Odia"},
  109. {0x49, "Tamil"},
  110. {0x4A, "Telugu"},
  111. {0x4B, "Kannada"},
  112. {0x4C, "Malayalam"},
  113. {0x4D, "Assamese"},
  114. {0x4E, "Marathi"},
  115. {0x4F, "Sanskrit"},
  116. {0x50, "Mongolian"},
  117. {0x51, "Tibetan"},
  118. {0x52, "Welsh"},
  119. {0x53, "Khmer"},
  120. {0x54, "Lao"},
  121. {0x55, "Burmese"},
  122. {0x56, "Galician"},
  123. {0x57, "Konkani"},
  124. {0x58, "Manipuri"},
  125. {0x59, "Sindhi"},
  126. {0x5A, "Syriac"},
  127. {0x5B, "Sinhala"},
  128. {0x5C, "Cherokee"},
  129. {0x5D, "Inuktitut"},
  130. {0x5E, "Amharic"},
  131. {0x5F, "Tamazight"},
  132. {0x60, "Kashmiri"},
  133. {0x61, "Nepali"},
  134. {0x62, "Frisian"},
  135. {0x63, "Pashto"},
  136. {0x64, "Filipino"},
  137. {0x65, "Divehi"},
  138. {0x66, "Edo"},
  139. {0x67, "Fulah"},
  140. {0x68, "Hausa"},
  141. {0x69, "Ibibio"},
  142. {0x6A, "Yoruba"},
  143. {0x6B, "Quechua"},
  144. {0x6C, "Sesotho sa Leboa"},
  145. {0x6D, "Bashkir"},
  146. {0x6E, "Luxembourgish"},
  147. {0x6F, "Greenlandic"},
  148. {0x70, "Igbo"},
  149. {0x71, "Kanuri"},
  150. {0x72, "Oromo"},
  151. {0x73, "Tigrinya"},
  152. {0x74, "Guarani"},
  153. {0x75, "Hawaiian"},
  154. {0x76, "Latin"},
  155. {0x77, "Somali"},
  156. {0x78, "Yi"},
  157. {0x79, "Papiamento"},
  158. {0x7A, "Mapudungun"},
  159. {0x7C, "Mohawk"},
  160. {0x7E, "Breton"},
  161. {0x7F, "Invariant Language (Invariant Country)"},
  162. {0x80, "Uyghur"},
  163. {0x81, "Maori"},
  164. {0x82, "Occitan"},
  165. {0x83, "Corsican"},
  166. {0x84, "Alsatian"},
  167. {0x85, "Sakha"},
  168. {0x86, "K’iche’"},
  169. {0x87, "Kinyarwanda"},
  170. {0x88, "Wolof"},
  171. {0x8C, "Dari"},
  172. {0x91, "Scottish Gaelic"},
  173. {0x92, "Central Kurdish"},
  174. {0x401, "Arabic (Saudi Arabia)"},
  175. {0x402, "Bulgarian (Bulgaria)"},
  176. {0x403, "Catalan (Catalan)"},
  177. {0x404, "Chinese (Traditional, Taiwan)"},
  178. {0x405, "Czech (Czech Republic)"},
  179. {0x406, "Danish (Denmark)"},
  180. {0x407, "German (Germany)"},
  181. {0x408, "Greek (Greece)"},
  182. {0x409, "English (United States)"},
  183. {0x40B, "Finnish (Finland)"},
  184. {0x40C, "French (France)"},
  185. {0x40D, "Hebrew (Israel)"},
  186. {0x40E, "Hungarian (Hungary)"},
  187. {0x40F, "Icelandic (Iceland)"},
  188. {0x410, "Italian (Italy)"},
  189. {0x411, "Japanese (Japan)"},
  190. {0x412, "Korean (Korea)"},
  191. {0x413, "Dutch (Netherlands)"},
  192. {0x414, "Norwegian, Bokmål (Norway)"},
  193. {0x415, "Polish (Poland)"},
  194. {0x416, "Portuguese (Brazil)"},
  195. {0x417, "Romansh (Switzerland)"},
  196. {0x418, "Romanian (Romania)"},
  197. {0x419, "Russian (Russia)"},
  198. {0x41A, "Croatian (Croatia)"},
  199. {0x41B, "Slovak (Slovakia)"},
  200. {0x41C, "Albanian (Albania)"},
  201. {0x41D, "Swedish (Sweden)"},
  202. {0x41E, "Thai (Thailand)"},
  203. {0x41F, "Turkish (Turkey)"},
  204. {0x420, "Urdu (Islamic Republic of Pakistan)"},
  205. {0x421, "Indonesian (Indonesia)"},
  206. {0x422, "Ukrainian (Ukraine)"},
  207. {0x423, "Belarusian (Belarus)"},
  208. {0x424, "Slovenian (Slovenia)"},
  209. {0x425, "Estonian (Estonia)"},
  210. {0x426, "Latvian (Latvia)"},
  211. {0x427, "Lithuanian (Lithuania)"},
  212. {0x428, "Tajik (Cyrillic, Tajikistan)"},
  213. {0x429, "Persian (Iran)"},
  214. {0x42A, "Vietnamese (Vietnam)"},
  215. {0x42B, "Armenian (Armenia)"},
  216. {0x42C, "Azerbaijani (Latin, Azerbaijan)"},
  217. {0x42D, "Basque (Basque)"},
  218. {0x42E, "Upper Sorbian (Germany)"},
  219. {0x42F, "Macedonian (Former Yugoslav Republic of Macedonia)"},
  220. {0x430, "Southern Sotho (South Africa)"},
  221. {0x431, "Tsonga (South Africa)"},
  222. {0x432, "Setswana (South Africa)"},
  223. {0x433, "Venda (South Africa)"},
  224. {0x434, "isiXhosa (South Africa)"},
  225. {0x435, "isiZulu (South Africa)"},
  226. {0x436, "Afrikaans (South Africa)"},
  227. {0x437, "Georgian (Georgia)"},
  228. {0x438, "Faroese (Faroe Islands)"},
  229. {0x439, "Hindi (India)"},
  230. {0x43A, "Maltese (Malta)"},
  231. {0x43B, "Sami, Northern (Norway)"},
  232. {0x43D, "Yiddish (World)"},
  233. {0x43E, "Malay (Malaysia)"},
  234. {0x43F, "Kazakh (Kazakhstan)"},
  235. {0x440, "Kyrgyz (Kyrgyzstan)"},
  236. {0x441, "Kiswahili (Kenya)"},
  237. {0x442, "Turkmen (Turkmenistan)"},
  238. {0x443, "Uzbek (Latin, Uzbekistan)"},
  239. {0x444, "Tatar (Russia)"},
  240. {0x445, "Bangla (India)"},
  241. {0x446, "Punjabi (India)"},
  242. {0x447, "Gujarati (India)"},
  243. {0x448, "Odia (India)"},
  244. {0x449, "Tamil (India)"},
  245. {0x44A, "Telugu (India)"},
  246. {0x44B, "Kannada (India)"},
  247. {0x44C, "Malayalam (India)"},
  248. {0x44D, "Assamese (India)"},
  249. {0x44E, "Marathi (India)"},
  250. {0x44F, "Sanskrit (India)"},
  251. {0x450, "Mongolian (Cyrillic, Mongolia)"},
  252. {0x451, "Tibetan (PRC)"},
  253. {0x452, "Welsh (United Kingdom)"},
  254. {0x453, "Khmer (Cambodia)"},
  255. {0x454, "Lao (Lao P.D.R.)"},
  256. {0x455, "Burmese (Myanmar)"},
  257. {0x456, "Galician (Galician)"},
  258. {0x457, "Konkani (India)"},
  259. {0x458, "Manipuri (India)"},
  260. {0x459, "Sindhi (Devanagari, India)"},
  261. {0x45A, "Syriac (Syria)"},
  262. {0x45B, "Sinhala (Sri Lanka)"},
  263. {0x45C, "Cherokee (Cherokee)"},
  264. {0x45D, "Inuktitut (Syllabics, Canada)"},
  265. {0x45E, "Amharic (Ethiopia)"},
  266. {0x45F, "Central Atlas Tamazight (Arabic, Morocco)"},
  267. {0x460, "Kashmiri (Perso-Arabic)"},
  268. {0x461, "Nepali (Nepal)"},
  269. {0x462, "Frisian (Netherlands)"},
  270. {0x463, "Pashto (Afghanistan)"},
  271. {0x464, "Filipino (Philippines)"},
  272. {0x465, "Divehi (Maldives)"},
  273. {0x466, "Edo (Nigeria)"},
  274. {0x467, "Fulah (Nigeria)"},
  275. {0x468, "Hausa (Latin, Nigeria)"},
  276. {0x469, "Ibibio (Nigeria)"},
  277. {0x46A, "Yoruba (Nigeria)"},
  278. {0x46B, "Quechua (Bolivia)"},
  279. {0x46C, "Sesotho sa Leboa (South Africa)"},
  280. {0x46D, "Bashkir (Russia)"},
  281. {0x46E, "Luxembourgish (Luxembourg)"},
  282. {0x46F, "Greenlandic (Greenland)"},
  283. {0x470, "Igbo (Nigeria)"},
  284. {0x471, "Kanuri (Nigeria)"},
  285. {0x472, "Oromo (Ethiopia)"},
  286. {0x473, "Tigrinya (Ethiopia)"},
  287. {0x474, "Guarani (Paraguay)"},
  288. {0x475, "Hawaiian (United States)"},
  289. {0x476, "Latin (World)"},
  290. {0x477, "Somali (Somalia)"},
  291. {0x478, "Yi (PRC)"},
  292. {0x479, "Papiamento (Caribbean)"},
  293. {0x47A, "Mapudungun (Chile)"},
  294. {0x47C, "Mohawk (Mohawk)"},
  295. {0x47E, "Breton (France)"},
  296. {0x480, "Uyghur (PRC)"},
  297. {0x481, "Maori (New Zealand)"},
  298. {0x482, "Occitan (France)"},
  299. {0x483, "Corsican (France)"},
  300. {0x484, "Alsatian (France)"},
  301. {0x485, "Sakha (Russia)"},
  302. {0x486, "K’iche’ (Guatemala)"},
  303. {0x487, "Kinyarwanda (Rwanda)"},
  304. {0x488, "Wolof (Senegal)"},
  305. {0x48C, "Dari (Afghanistan)"},
  306. {0x491, "Scottish Gaelic (United Kingdom)"},
  307. {0x492, "Central Kurdish (Iraq)"},
  308. {0x801, "Arabic (Iraq)"},
  309. {0x803, "Valencian (Spain)"},
  310. {0x804, "Chinese (Simplified, PRC)"},
  311. {0x807, "German (Switzerland)"},
  312. {0x809, "English (United Kingdom)"},
  313. {0x80A, "Spanish (Mexico)"},
  314. {0x80C, "French (Belgium)"},
  315. {0x810, "Italian (Switzerland)"},
  316. {0x813, "Dutch (Belgium)"},
  317. {0x814, "Norwegian, Nynorsk (Norway)"},
  318. {0x816, "Portuguese (Portugal)"},
  319. {0x818, "Romanian (Moldova)"},
  320. {0x819, "Russian (Moldova)"},
  321. {0x81D, "Swedish (Finland)"},
  322. {0x820, "Urdu (India)"},
  323. {0x82C, "Azerbaijani (Cyrillic, Azerbaijan)"},
  324. {0x82E, "Lower Sorbian (Germany)"},
  325. {0x832, "Setswana (Botswana)"},
  326. {0x83B, "Sami, Northern (Sweden)"},
  327. {0x83C, "Irish (Ireland)"},
  328. {0x83E, "Malay (Brunei Darussalam)"},
  329. {0x843, "Uzbek (Cyrillic, Uzbekistan)"},
  330. {0x845, "Bangla (Bangladesh)"},
  331. {0x846, "Punjabi (Islamic Republic of Pakistan)"},
  332. {0x849, "Tamil (Sri Lanka)"},
  333. {0x850, "Mongolian (Traditional Mongolian, PRC)"},
  334. {0x859, "Sindhi (Islamic Republic of Pakistan)"},
  335. {0x85D, "Inuktitut (Latin, Canada)"},
  336. {0x85F, "Tamazight (Latin, Algeria)"},
  337. {0x860, "Kashmiri (Devanagari, India)"},
  338. {0x861, "Nepali (India)"},
  339. {0x867, "Fulah (Latin, Senegal)"},
  340. {0x86B, "Quechua (Ecuador)"},
  341. {0x873, "Tigrinya (Eritrea)"},
  342. {0xC01, "Arabic (Egypt)"},
  343. {0xC04, "Chinese (Traditional, Hong Kong S.A.R.)"},
  344. {0xC07, "German (Austria)"},
  345. {0xC09, "English (Australia)"},
  346. {0xC0A, "Spanish (Spain)"},
  347. {0xC0C, "French (Canada)"},
  348. {0xC3B, "Sami, Northern (Finland)"},
  349. {0xC50, "Mongolian (Traditional Mongolian, Mongolia)"},
  350. {0xC51, "Dzongkha (Bhutan)"},
  351. {0xC6B, "Quechua (Peru)"},
  352. {0x1001, "Arabic (Libya)"},
  353. {0x1004, "Chinese (Simplified, Singapore)"},
  354. {0x1007, "German (Luxembourg)"},
  355. {0x1009, "English (Canada)"},
  356. {0x100A, "Spanish (Guatemala)"},
  357. {0x100C, "French (Switzerland)"},
  358. {0x101A, "Croatian (Latin, Bosnia and Herzegovina)"},
  359. {0x103B, "Sami, Lule (Norway)"},
  360. {0x105F, "Central Atlas Tamazight (Tifinagh, Morocco)"},
  361. {0x1401, "Arabic (Algeria)"},
  362. {0x1404, "Chinese (Traditional, Macao S.A.R.)"},
  363. {0x1407, "German (Liechtenstein)"},
  364. {0x1409, "English (New Zealand)"},
  365. {0x140A, "Spanish (Costa Rica)"},
  366. {0x140C, "French (Luxembourg)"},
  367. {0x141A, "Bosnian (Latin, Bosnia and Herzegovina)"},
  368. {0x143B, "Sami, Lule (Sweden)"},
  369. {0x1801, "Arabic (Morocco)"},
  370. {0x1809, "English (Ireland)"},
  371. {0x180A, "Spanish (Panama)"},
  372. {0x180C, "French (Monaco)"},
  373. {0x181A, "Serbian (Latin, Bosnia and Herzegovina)"},
  374. {0x183B, "Sami, Southern (Norway)"},
  375. {0x1C01, "Arabic (Tunisia)"},
  376. {0x1C09, "English (South Africa)"},
  377. {0x1C0A, "Spanish (Dominican Republic)"},
  378. {0x1C0C, "French (Caribbean)"},
  379. {0x1C1A, "Serbian (Cyrillic, Bosnia and Herzegovina)"},
  380. {0x1C3B, "Sami, Southern (Sweden)"},
  381. {0x2001, "Arabic (Oman)"},
  382. {0x2009, "English (Jamaica)"},
  383. {0x200A, "Spanish (Venezuela)"},
  384. {0x200C, "French (Reunion)"},
  385. {0x201A, "Bosnian (Cyrillic, Bosnia and Herzegovina)"},
  386. {0x203B, "Sami, Skolt (Finland)"},
  387. {0x2401, "Arabic (Yemen)"},
  388. {0x2409, "English (Caribbean)"},
  389. {0x240A, "Spanish (Colombia)"},
  390. {0x240C, "French (Congo DRC)"},
  391. {0x241A, "Serbian (Latin, Serbia)"},
  392. {0x243B, "Sami, Inari (Finland)"},
  393. {0x2801, "Arabic (Syria)"},
  394. {0x2809, "English (Belize)"},
  395. {0x280A, "Spanish (Peru)"},
  396. {0x280C, "French (Senegal)"},
  397. {0x281A, "Serbian (Cyrillic, Serbia)"},
  398. {0x2C01, "Arabic (Jordan)"},
  399. {0x2C09, "English (Trinidad and Tobago)"},
  400. {0x2C0A, "Spanish (Argentina)"},
  401. {0x2C0C, "French (Cameroon)"},
  402. {0x2C1A, "Serbian (Latin, Montenegro)"},
  403. {0x3001, "Arabic (Lebanon)"},
  404. {0x3009, "English (Zimbabwe)"},
  405. {0x300A, "Spanish (Ecuador)"},
  406. {0x300C, "French (Côte d’Ivoire)"},
  407. {0x301A, "Serbian (Cyrillic, Montenegro)"},
  408. {0x3401, "Arabic (Kuwait)"},
  409. {0x3409, "English (Philippines)"},
  410. {0x340A, "Spanish (Chile)"},
  411. {0x340C, "French (Mali)"},
  412. {0x3801, "Arabic (U.A.E.)"},
  413. {0x3809, "English (Indonesia)"},
  414. {0x380A, "Spanish (Uruguay)"},
  415. {0x380C, "French (Morocco)"},
  416. {0x3C01, "Arabic (Bahrain)"},
  417. {0x3C09, "English (Hong Kong SAR)"},
  418. {0x3C0A, "Spanish (Paraguay)"},
  419. {0x3C0C, "French (Haiti)"},
  420. {0x4001, "Arabic (Qatar)"},
  421. {0x4009, "English (India)"},
  422. {0x400A, "Spanish (Bolivia)"},
  423. {0x4409, "English (Malaysia)"},
  424. {0x440A, "Spanish (El Salvador)"},
  425. {0x4809, "English (Singapore)"},
  426. {0x480A, "Spanish (Honduras)"},
  427. {0x4C0A, "Spanish (Nicaragua)"},
  428. {0x500A, "Spanish (Puerto Rico)"},
  429. {0x540A, "Spanish (United States)"},
  430. {0x580A, "Spanish (Latin America)"},
  431. {0x5C0A, "Spanish (Cuba)"},
  432. {0x641A, "Bosnian (Cyrillic)"},
  433. {0x681A, "Bosnian (Latin)"},
  434. {0x6C1A, "Serbian (Cyrillic)"},
  435. {0x701A, "Serbian (Latin)"},
  436. {0x703B, "Sami (Inari)"},
  437. {0x742C, "Azerbaijani (Cyrillic)"},
  438. {0x743B, "Sami (Skolt)"},
  439. {0x7804, "Chinese"},
  440. {0x7814, "Norwegian (Nynorsk)"},
  441. {0x781A, "Bosnian"},
  442. {0x782C, "Azerbaijani (Latin)"},
  443. {0x783B, "Sami (Southern)"},
  444. {0x7843, "Uzbek (Cyrillic)"},
  445. {0x7850, "Mongolian (Cyrillic)"},
  446. {0x785D, "Inuktitut (Syllabics)"},
  447. {0x785F, "Tamazight (Tifinagh)"},
  448. {0x7C04, "Chinese (Traditional)"},
  449. {0x7C04, "Chinese (Traditional) Legacy"},
  450. {0x7C14, "Norwegian (Bokmål)"},
  451. {0x7C1A, "Serbian"},
  452. {0x7C28, "Tajik (Cyrillic)"},
  453. {0x7C2E, "Lower Sorbian"},
  454. {0x7C3B, "Sami (Lule)"},
  455. {0x7C43, "Uzbek (Latin)"},
  456. {0x7C46, "Punjabi (Arabic)"},
  457. {0x7C50, "Mongolian (Traditional Mongolian)"},
  458. {0x7C59, "Sindhi (Arabic)"},
  459. {0x7C5C, "Cherokee (Cherokee)"},
  460. {0x7C5D, "Inuktitut (Latin)"},
  461. {0x7C5F, "Tamazight (Latin)"},
  462. {0x7C67, "Fulah (Latin)"},
  463. {0x7C68, "Hausa (Latin)"},
  464. {0x7C86, "K’iche’"},
  465. {0x7C92, "Central Kurdish (Arabic)"},
  466. {0xF400, "System Default for Time"},
  467. {0xF800, "System Default for Long Date"},
  468. });
  469. return all;
  470. }
  471. [[noreturn]] void unhandled_case_error()
  472. {
  473. throw xlnt::exception("unhandled");
  474. }
  475. void unhandled_case(bool error)
  476. {
  477. if (error)
  478. {
  479. unhandled_case_error();
  480. }
  481. }
  482. } // namespace
  483. namespace xlnt {
  484. namespace detail {
  485. bool format_condition::satisfied_by(double number) const
  486. {
  487. switch (type)
  488. {
  489. case condition_type::greater_or_equal:
  490. return number >= value;
  491. case condition_type::greater_than:
  492. return number > value;
  493. case condition_type::less_or_equal:
  494. return number <= value;
  495. case condition_type::less_than:
  496. return number < value;
  497. case condition_type::not_equal:
  498. return std::fabs(number - value) != 0.0;
  499. case condition_type::equal:
  500. return std::fabs(number - value) == 0.0;
  501. }
  502. default_case(false);
  503. }
  504. number_format_parser::number_format_parser(const std::string &format_string)
  505. {
  506. reset(format_string);
  507. }
  508. const std::vector<format_code> &number_format_parser::result() const
  509. {
  510. return codes_;
  511. }
  512. void number_format_parser::reset(const std::string &format_string)
  513. {
  514. format_string_ = format_string;
  515. position_ = 0;
  516. codes_.clear();
  517. }
  518. void number_format_parser::parse()
  519. {
  520. auto token = parse_next_token();
  521. format_code section;
  522. template_part part;
  523. for (;;)
  524. {
  525. switch (token.type)
  526. {
  527. case number_format_token::token_type::end_section: {
  528. codes_.push_back(section);
  529. section = format_code();
  530. break;
  531. }
  532. case number_format_token::token_type::color: {
  533. if (section.has_color || section.has_condition || section.has_locale || !section.parts.empty())
  534. {
  535. throw xlnt::exception("color should be the first part of a format");
  536. }
  537. section.has_color = true;
  538. section.color = color_from_string(token.string);
  539. break;
  540. }
  541. case number_format_token::token_type::locale: {
  542. if (section.has_locale)
  543. {
  544. throw xlnt::exception("multiple locales");
  545. }
  546. section.has_locale = true;
  547. auto parsed_locale = locale_from_string(token.string);
  548. section.locale = parsed_locale.first;
  549. if (!parsed_locale.second.empty())
  550. {
  551. part.type = template_part::template_type::text;
  552. part.string = parsed_locale.second;
  553. section.parts.push_back(part);
  554. part = template_part();
  555. }
  556. break;
  557. }
  558. case number_format_token::token_type::condition: {
  559. if (section.has_condition)
  560. {
  561. throw xlnt::exception("multiple conditions");
  562. }
  563. section.has_condition = true;
  564. std::string value;
  565. if (token.string.front() == '<')
  566. {
  567. if (token.string[1] == '=')
  568. {
  569. section.condition.type = format_condition::condition_type::less_or_equal;
  570. value = token.string.substr(2);
  571. }
  572. else if (token.string[1] == '>')
  573. {
  574. section.condition.type = format_condition::condition_type::not_equal;
  575. value = token.string.substr(2);
  576. }
  577. else
  578. {
  579. section.condition.type = format_condition::condition_type::less_than;
  580. value = token.string.substr(1);
  581. }
  582. }
  583. else if (token.string.front() == '>')
  584. {
  585. if (token.string[1] == '=')
  586. {
  587. section.condition.type = format_condition::condition_type::greater_or_equal;
  588. value = token.string.substr(2);
  589. }
  590. else
  591. {
  592. section.condition.type = format_condition::condition_type::greater_than;
  593. value = token.string.substr(1);
  594. }
  595. }
  596. else if (token.string.front() == '=')
  597. {
  598. section.condition.type = format_condition::condition_type::equal;
  599. value = token.string.substr(1);
  600. }
  601. detail::number_serialiser ser;
  602. section.condition.value = ser.deserialise(value);
  603. break;
  604. }
  605. case number_format_token::token_type::text: {
  606. part.type = template_part::template_type::text;
  607. part.string = token.string;
  608. section.parts.push_back(part);
  609. part = template_part();
  610. break;
  611. }
  612. case number_format_token::token_type::fill: {
  613. part.type = template_part::template_type::fill;
  614. part.string = token.string;
  615. section.parts.push_back(part);
  616. part = template_part();
  617. break;
  618. }
  619. case number_format_token::token_type::space: {
  620. part.type = template_part::template_type::space;
  621. part.string = token.string;
  622. section.parts.push_back(part);
  623. part = template_part();
  624. break;
  625. }
  626. case number_format_token::token_type::number: {
  627. part.type = template_part::template_type::general;
  628. part.placeholders = parse_placeholders(token.string);
  629. section.parts.push_back(part);
  630. part = template_part();
  631. break;
  632. }
  633. case number_format_token::token_type::datetime: {
  634. section.is_datetime = true;
  635. switch (token.string.front())
  636. {
  637. case '[':
  638. section.is_timedelta = true;
  639. if (token.string == "[h]" || token.string == "[hh]")
  640. {
  641. part.type = template_part::template_type::elapsed_hours;
  642. break;
  643. }
  644. else if (token.string == "[m]" || token.string == "[mm]")
  645. {
  646. part.type = template_part::template_type::elapsed_minutes;
  647. break;
  648. }
  649. else if (token.string == "[s]" || token.string == "[ss]")
  650. {
  651. part.type = template_part::template_type::elapsed_seconds;
  652. break;
  653. }
  654. unhandled_case(true);
  655. break;
  656. case 'm':
  657. if (token.string == "m")
  658. {
  659. part.type = template_part::template_type::month_number;
  660. break;
  661. }
  662. else if (token.string == "mm")
  663. {
  664. part.type = template_part::template_type::month_number_leading_zero;
  665. break;
  666. }
  667. else if (token.string == "mmm")
  668. {
  669. part.type = template_part::template_type::month_abbreviation;
  670. break;
  671. }
  672. else if (token.string == "mmmm")
  673. {
  674. part.type = template_part::template_type::month_name;
  675. break;
  676. }
  677. else if (token.string == "mmmmm")
  678. {
  679. part.type = template_part::template_type::month_letter;
  680. break;
  681. }
  682. unhandled_case(true);
  683. break;
  684. case 'd':
  685. if (token.string == "d")
  686. {
  687. part.type = template_part::template_type::day_number;
  688. break;
  689. }
  690. else if (token.string == "dd")
  691. {
  692. part.type = template_part::template_type::day_number_leading_zero;
  693. break;
  694. }
  695. else if (token.string == "ddd")
  696. {
  697. part.type = template_part::template_type::day_abbreviation;
  698. break;
  699. }
  700. else if (token.string == "dddd")
  701. {
  702. part.type = template_part::template_type::day_name;
  703. break;
  704. }
  705. unhandled_case(true);
  706. break;
  707. case 'y':
  708. if (token.string == "yy")
  709. {
  710. part.type = template_part::template_type::year_short;
  711. break;
  712. }
  713. else if (token.string == "yyyy")
  714. {
  715. part.type = template_part::template_type::year_long;
  716. break;
  717. }
  718. unhandled_case(true);
  719. break;
  720. case 'h':
  721. if (token.string == "h")
  722. {
  723. part.type = template_part::template_type::hour;
  724. break;
  725. }
  726. else if (token.string == "hh")
  727. {
  728. part.type = template_part::template_type::hour_leading_zero;
  729. break;
  730. }
  731. unhandled_case(true);
  732. break;
  733. case 's':
  734. if (token.string == "s")
  735. {
  736. part.type = template_part::template_type::second;
  737. break;
  738. }
  739. else if (token.string == "ss")
  740. {
  741. part.type = template_part::template_type::second_leading_zero;
  742. break;
  743. }
  744. unhandled_case(true);
  745. break;
  746. case 'A':
  747. section.twelve_hour = true;
  748. if (token.string == "AM/PM")
  749. {
  750. part.type = template_part::template_type::am_pm;
  751. break;
  752. }
  753. else if (token.string == "A/P")
  754. {
  755. part.type = template_part::template_type::a_p;
  756. break;
  757. }
  758. unhandled_case(true);
  759. break;
  760. default:
  761. unhandled_case(true);
  762. break;
  763. }
  764. section.parts.push_back(part);
  765. part = template_part();
  766. break;
  767. }
  768. case number_format_token::token_type::end: {
  769. codes_.push_back(section);
  770. finalize();
  771. return;
  772. }
  773. }
  774. token = parse_next_token();
  775. }
  776. }
  777. void number_format_parser::finalize()
  778. {
  779. for (auto &code : codes_)
  780. {
  781. bool fix = false;
  782. bool leading_zero = false;
  783. std::size_t minutes_index = 0;
  784. bool integer_part = false;
  785. bool fractional_part = false;
  786. std::size_t integer_part_index = 0;
  787. bool percentage = false;
  788. bool exponent = false;
  789. std::size_t exponent_index = 0;
  790. bool fraction = false;
  791. std::size_t fraction_denominator_index = 0;
  792. std::size_t fraction_numerator_index = 0;
  793. bool seconds = false;
  794. bool fractional_seconds = false;
  795. std::size_t seconds_index = 0;
  796. for (std::size_t i = 0; i < code.parts.size(); ++i)
  797. {
  798. const auto &part = code.parts[i];
  799. if (i > 0 && i + 1 < code.parts.size() && part.type == template_part::template_type::text
  800. && part.string == "/"
  801. && code.parts[i - 1].placeholders.type == format_placeholders::placeholders_type::integer_part
  802. && code.parts[i + 1].placeholders.type == format_placeholders::placeholders_type::integer_part)
  803. {
  804. fraction = true;
  805. fraction_numerator_index = i - 1;
  806. fraction_denominator_index = i + 1;
  807. }
  808. if (part.placeholders.type == format_placeholders::placeholders_type::integer_part)
  809. {
  810. integer_part = true;
  811. integer_part_index = i;
  812. }
  813. else if (part.placeholders.type == format_placeholders::placeholders_type::fractional_part)
  814. {
  815. fractional_part = true;
  816. }
  817. else if (part.placeholders.type == format_placeholders::placeholders_type::scientific_exponent_plus
  818. || part.placeholders.type == format_placeholders::placeholders_type::scientific_exponent_minus)
  819. {
  820. exponent = true;
  821. exponent_index = i;
  822. }
  823. if (part.placeholders.percentage)
  824. {
  825. percentage = true;
  826. }
  827. if (part.type == template_part::template_type::second
  828. || part.type == template_part::template_type::second_leading_zero)
  829. {
  830. seconds = true;
  831. seconds_index = i;
  832. }
  833. if (seconds && part.placeholders.type == format_placeholders::placeholders_type::fractional_part)
  834. {
  835. fractional_seconds = true;
  836. }
  837. // TODO this block needs improvement
  838. if (part.type == template_part::template_type::month_number
  839. || part.type == template_part::template_type::month_number_leading_zero)
  840. {
  841. if (code.parts.size() > 1 && i < code.parts.size() - 2)
  842. {
  843. const auto &next = code.parts[i + 1];
  844. const auto &after_next = code.parts[i + 2];
  845. if ((next.type == template_part::template_type::second
  846. || next.type == template_part::template_type::second_leading_zero)
  847. || (next.type == template_part::template_type::text && next.string == ":"
  848. && (after_next.type == template_part::template_type::second
  849. || after_next.type == template_part::template_type::second_leading_zero)))
  850. {
  851. fix = true;
  852. leading_zero = part.type == template_part::template_type::month_number_leading_zero;
  853. minutes_index = i;
  854. }
  855. }
  856. if (!fix && i > 1)
  857. {
  858. const auto &previous = code.parts[i - 1];
  859. const auto &before_previous = code.parts[i - 2];
  860. if (previous.type == template_part::template_type::text && previous.string == ":"
  861. && (before_previous.type == template_part::template_type::hour_leading_zero
  862. || before_previous.type == template_part::template_type::hour))
  863. {
  864. fix = true;
  865. leading_zero = part.type == template_part::template_type::month_number_leading_zero;
  866. minutes_index = i;
  867. }
  868. }
  869. }
  870. }
  871. if (fix)
  872. {
  873. code.parts[minutes_index].type =
  874. leading_zero ? template_part::template_type::minute_leading_zero : template_part::template_type::minute;
  875. }
  876. if (integer_part && !fractional_part)
  877. {
  878. code.parts[integer_part_index].placeholders.type = format_placeholders::placeholders_type::integer_only;
  879. }
  880. if (integer_part && fractional_part && percentage)
  881. {
  882. code.parts[integer_part_index].placeholders.percentage = true;
  883. }
  884. if (exponent)
  885. {
  886. const auto &next = code.parts[exponent_index + 1];
  887. auto temp = code.parts[exponent_index].placeholders.type;
  888. code.parts[exponent_index].placeholders = next.placeholders;
  889. code.parts[exponent_index].placeholders.type = temp;
  890. code.parts.erase(code.parts.begin() + static_cast<std::ptrdiff_t>(exponent_index + 1));
  891. for (std::size_t i = 0; i < code.parts.size(); ++i)
  892. {
  893. code.parts[i].placeholders.scientific = true;
  894. }
  895. }
  896. if (fraction)
  897. {
  898. code.parts[fraction_numerator_index].placeholders.type =
  899. format_placeholders::placeholders_type::fraction_numerator;
  900. code.parts[fraction_denominator_index].placeholders.type =
  901. format_placeholders::placeholders_type::fraction_denominator;
  902. for (std::size_t i = 0; i < code.parts.size(); ++i)
  903. {
  904. if (code.parts[i].placeholders.type == format_placeholders::placeholders_type::integer_part)
  905. {
  906. code.parts[i].placeholders.type = format_placeholders::placeholders_type::fraction_integer;
  907. }
  908. }
  909. }
  910. if (fractional_seconds)
  911. {
  912. if (code.parts[seconds_index].type == template_part::template_type::second)
  913. {
  914. code.parts[seconds_index].type = template_part::template_type::second_fractional;
  915. }
  916. else
  917. {
  918. code.parts[seconds_index].type = template_part::template_type::second_leading_zero_fractional;
  919. }
  920. }
  921. }
  922. validate();
  923. }
  924. number_format_token number_format_parser::parse_next_token()
  925. {
  926. number_format_token token;
  927. auto to_lower = [](char c) {
  928. return static_cast<char>(std::tolower(static_cast<std::uint8_t>(c)));
  929. };
  930. if (format_string_.size() <= position_)
  931. {
  932. token.type = number_format_token::token_type::end;
  933. return token;
  934. }
  935. auto current_char = format_string_[position_++];
  936. switch (current_char)
  937. {
  938. case '[':
  939. if (position_ == format_string_.size())
  940. {
  941. throw xlnt::exception("missing ]");
  942. }
  943. if (format_string_[position_] == ']')
  944. {
  945. throw xlnt::exception("empty []");
  946. }
  947. do
  948. {
  949. token.string.push_back(format_string_[position_++]);
  950. } while (position_ < format_string_.size() && format_string_[position_] != ']');
  951. if (token.string[0] == '<' || token.string[0] == '>' || token.string[0] == '=')
  952. {
  953. token.type = number_format_token::token_type::condition;
  954. }
  955. else if (token.string[0] == '$')
  956. {
  957. token.type = number_format_token::token_type::locale;
  958. }
  959. else if (token.string.size() <= 2
  960. && ((token.string == "h" || token.string == "hh") || (token.string == "m" || token.string == "mm")
  961. || (token.string == "s" || token.string == "ss")))
  962. {
  963. token.type = number_format_token::token_type::datetime;
  964. token.string = "[" + token.string + "]";
  965. }
  966. else
  967. {
  968. token.type = number_format_token::token_type::color;
  969. color_from_string(token.string);
  970. }
  971. ++position_;
  972. break;
  973. case '\\':
  974. token.type = number_format_token::token_type::text;
  975. token.string.push_back(format_string_[position_++]);
  976. break;
  977. case 'G':
  978. if (format_string_.substr(position_ - 1, 7) != "General")
  979. {
  980. throw xlnt::exception("expected General");
  981. }
  982. token.type = number_format_token::token_type::number;
  983. token.string = "General";
  984. position_ += 6;
  985. break;
  986. case '_':
  987. token.type = number_format_token::token_type::space;
  988. token.string.push_back(format_string_[position_++]);
  989. break;
  990. case '*':
  991. token.type = number_format_token::token_type::fill;
  992. token.string.push_back(format_string_[position_++]);
  993. break;
  994. case '0':
  995. case '#':
  996. case '?':
  997. case '.':
  998. token.type = number_format_token::token_type::number;
  999. do
  1000. {
  1001. token.string.push_back(current_char);
  1002. current_char = format_string_[position_++];
  1003. } while (current_char == '0' || current_char == '#' || current_char == '?' || current_char == ',');
  1004. --position_;
  1005. if (current_char == '%')
  1006. {
  1007. token.string.push_back('%');
  1008. ++position_;
  1009. }
  1010. break;
  1011. case 'y':
  1012. case 'Y':
  1013. case 'm':
  1014. case 'M':
  1015. case 'd':
  1016. case 'D':
  1017. case 'h':
  1018. case 'H':
  1019. case 's':
  1020. case 'S':
  1021. token.type = number_format_token::token_type::datetime;
  1022. token.string.push_back(to_lower(current_char));
  1023. while (format_string_[position_] == current_char)
  1024. {
  1025. token.string.push_back(to_lower(current_char));
  1026. ++position_;
  1027. }
  1028. break;
  1029. case 'A':
  1030. token.type = number_format_token::token_type::datetime;
  1031. if (format_string_.substr(position_ - 1, 5) == "AM/PM")
  1032. {
  1033. position_ += 4;
  1034. token.string = "AM/PM";
  1035. }
  1036. else if (format_string_.substr(position_ - 1, 3) == "A/P")
  1037. {
  1038. position_ += 2;
  1039. token.string = "A/P";
  1040. }
  1041. else
  1042. {
  1043. throw xlnt::exception("expected AM/PM or A/P");
  1044. }
  1045. break;
  1046. case '"': {
  1047. token.type = number_format_token::token_type::text;
  1048. auto start = position_;
  1049. auto end = format_string_.find('"', position_);
  1050. while (end != std::string::npos && format_string_[end - 1] == '\\')
  1051. {
  1052. token.string.append(format_string_.substr(start, end - start - 1));
  1053. token.string.push_back('"');
  1054. position_ = end + 1;
  1055. start = position_;
  1056. end = format_string_.find('"', position_);
  1057. }
  1058. if (end != start)
  1059. {
  1060. token.string.append(format_string_.substr(start, end - start));
  1061. }
  1062. position_ = end + 1;
  1063. break;
  1064. }
  1065. case ';':
  1066. token.type = number_format_token::token_type::end_section;
  1067. break;
  1068. case '(':
  1069. token.type = number_format_token::token_type::text;
  1070. token.string.push_back(current_char);
  1071. break;
  1072. case ')':
  1073. token.type = number_format_token::token_type::text;
  1074. token.string.push_back(current_char);
  1075. break;
  1076. case '-':
  1077. token.type = number_format_token::token_type::text;
  1078. token.string.push_back(current_char);
  1079. break;
  1080. case '+':
  1081. token.type = number_format_token::token_type::text;
  1082. token.string.push_back(current_char);
  1083. break;
  1084. case ':':
  1085. token.type = number_format_token::token_type::text;
  1086. token.string.push_back(current_char);
  1087. break;
  1088. case ' ':
  1089. token.type = number_format_token::token_type::text;
  1090. token.string.push_back(current_char);
  1091. break;
  1092. case '/':
  1093. token.type = number_format_token::token_type::text;
  1094. token.string.push_back(current_char);
  1095. break;
  1096. case '@':
  1097. token.type = number_format_token::token_type::number;
  1098. token.string.push_back(current_char);
  1099. break;
  1100. case 'E':
  1101. token.type = number_format_token::token_type::number;
  1102. token.string.push_back(current_char);
  1103. current_char = format_string_[position_++];
  1104. if (current_char == '+' || current_char == '-')
  1105. {
  1106. token.string.push_back(current_char);
  1107. break;
  1108. }
  1109. break;
  1110. default:
  1111. throw xlnt::exception("unexpected character");
  1112. }
  1113. return token;
  1114. }
  1115. void number_format_parser::validate()
  1116. {
  1117. if (codes_.size() > 4)
  1118. {
  1119. throw xlnt::exception("too many format codes");
  1120. }
  1121. if (codes_.size() > 2)
  1122. {
  1123. if (codes_[0].has_condition && codes_[1].has_condition && codes_[2].has_condition)
  1124. {
  1125. throw xlnt::exception("format should have a maximum of two codes with conditions");
  1126. }
  1127. }
  1128. }
  1129. format_placeholders number_format_parser::parse_placeholders(const std::string &placeholders_string)
  1130. {
  1131. format_placeholders p;
  1132. if (placeholders_string == "General")
  1133. {
  1134. p.type = format_placeholders::placeholders_type::general;
  1135. return p;
  1136. }
  1137. else if (placeholders_string == "@")
  1138. {
  1139. p.type = format_placeholders::placeholders_type::text;
  1140. return p;
  1141. }
  1142. else if (placeholders_string.front() == '.')
  1143. {
  1144. p.type = format_placeholders::placeholders_type::fractional_part;
  1145. }
  1146. else if (placeholders_string.front() == 'E')
  1147. {
  1148. p.type = placeholders_string[1] == '+' ? format_placeholders::placeholders_type::scientific_exponent_plus
  1149. : format_placeholders::placeholders_type::scientific_exponent_minus;
  1150. return p;
  1151. }
  1152. else
  1153. {
  1154. p.type = format_placeholders::placeholders_type::integer_part;
  1155. }
  1156. if (placeholders_string.back() == '%')
  1157. {
  1158. p.percentage = true;
  1159. }
  1160. std::vector<std::size_t> comma_indices;
  1161. for (std::size_t i = 0; i < placeholders_string.size(); ++i)
  1162. {
  1163. auto c = placeholders_string[i];
  1164. if (c == '0')
  1165. {
  1166. ++p.num_zeros;
  1167. }
  1168. else if (c == '#')
  1169. {
  1170. ++p.num_optionals;
  1171. }
  1172. else if (c == '?')
  1173. {
  1174. ++p.num_spaces;
  1175. }
  1176. else if (c == ',')
  1177. {
  1178. comma_indices.push_back(i);
  1179. }
  1180. }
  1181. if (!comma_indices.empty())
  1182. {
  1183. std::size_t i = placeholders_string.size() - 1;
  1184. while (!comma_indices.empty() && i == comma_indices.back())
  1185. {
  1186. ++p.thousands_scale;
  1187. --i;
  1188. comma_indices.pop_back();
  1189. }
  1190. p.use_comma_separator = !comma_indices.empty();
  1191. }
  1192. return p;
  1193. }
  1194. format_color number_format_parser::color_from_string(const std::string &color)
  1195. {
  1196. switch (color[0])
  1197. {
  1198. case 'C':
  1199. if ((color == "Cyan") || (color == "CYAN"))
  1200. {
  1201. return format_color::cyan;
  1202. }
  1203. else if ((color.substr(0, 5) == "Color") || (color.substr(0, 5) == "COLOR"))
  1204. {
  1205. auto color_number = std::stoull(color.substr(5));
  1206. if (color_number >= 1 && color_number <= 56)
  1207. {
  1208. return static_cast<format_color>(color_number);
  1209. }
  1210. }
  1211. unhandled_case(true);
  1212. break;
  1213. case 'B':
  1214. if ((color == "Black") || (color == "BLACK"))
  1215. {
  1216. return format_color::black;
  1217. }
  1218. else if ((color == "Blue") || (color == "BLUE"))
  1219. {
  1220. return format_color::blue;
  1221. }
  1222. unhandled_case(true);
  1223. break;
  1224. case 'G':
  1225. if ((color == "Green") || (color == "GREEN"))
  1226. {
  1227. return format_color::green;
  1228. }
  1229. unhandled_case(true);
  1230. break;
  1231. case 'W':
  1232. if ((color == "White") || (color == "WHITE"))
  1233. {
  1234. return format_color::white;
  1235. }
  1236. unhandled_case(true);
  1237. break;
  1238. case 'M':
  1239. if ((color == "Magenta") || (color == "MAGENTA"))
  1240. {
  1241. return format_color::magenta;
  1242. }
  1243. unhandled_case(true);
  1244. break;
  1245. case 'Y':
  1246. if ((color == "Yellow") || (color == "YELLOW"))
  1247. {
  1248. return format_color::yellow;
  1249. }
  1250. unhandled_case(true);
  1251. break;
  1252. case 'R':
  1253. if ((color == "Red") || (color == "RED"))
  1254. {
  1255. return format_color::red;
  1256. }
  1257. unhandled_case(true);
  1258. break;
  1259. default:
  1260. unhandled_case(true);
  1261. }
  1262. unhandled_case_error();
  1263. }
  1264. std::pair<format_locale, std::string> number_format_parser::locale_from_string(const std::string &locale_string)
  1265. {
  1266. auto hyphen_index = locale_string.find('-');
  1267. if (locale_string.empty() || locale_string.front() != '$' || hyphen_index == std::string::npos)
  1268. {
  1269. throw xlnt::exception("bad locale: " + locale_string);
  1270. }
  1271. std::pair<format_locale, std::string> result;
  1272. if (hyphen_index > 1)
  1273. {
  1274. result.second = locale_string.substr(1, hyphen_index - 1);
  1275. }
  1276. auto country_code_string = locale_string.substr(hyphen_index + 1);
  1277. if (country_code_string.empty())
  1278. {
  1279. throw xlnt::exception("bad locale: " + locale_string);
  1280. }
  1281. for (auto c : country_code_string)
  1282. {
  1283. if (!((c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') || (c >= '0' && c <= '9')))
  1284. {
  1285. throw xlnt::exception("bad locale: " + locale_string);
  1286. }
  1287. }
  1288. auto country_code = std::stoi(country_code_string, nullptr, 16);
  1289. country_code &= 0xFFFF;
  1290. for (const auto &known_locale : known_locales())
  1291. {
  1292. if (known_locale.first == country_code)
  1293. {
  1294. result.first = static_cast<format_locale>(country_code);
  1295. return result;
  1296. }
  1297. }
  1298. throw xlnt::exception("unknown country code: " + country_code_string);
  1299. }
  1300. number_formatter::number_formatter(const std::string &format_string, xlnt::calendar calendar)
  1301. : parser_(format_string), calendar_(calendar)
  1302. {
  1303. parser_.parse();
  1304. format_ = parser_.result();
  1305. }
  1306. std::string number_formatter::format_number(double number)
  1307. {
  1308. if (format_[0].has_condition)
  1309. {
  1310. if (format_[0].condition.satisfied_by(number))
  1311. {
  1312. return format_number(format_[0], number);
  1313. }
  1314. if (format_.size() == 1)
  1315. {
  1316. return std::string(11, '#');
  1317. }
  1318. if (!format_[1].has_condition || format_[1].condition.satisfied_by(number))
  1319. {
  1320. return format_number(format_[1], number);
  1321. }
  1322. if (format_.size() == 2)
  1323. {
  1324. return std::string(11, '#');
  1325. }
  1326. return format_number(format_[2], number);
  1327. }
  1328. // no conditions, format based on sign:
  1329. // 1 section, use for all
  1330. if (format_.size() == 1)
  1331. {
  1332. return format_number(format_[0], number);
  1333. }
  1334. // 2 sections, first for positive and zero, second for negative
  1335. else if (format_.size() == 2)
  1336. {
  1337. if (number >= 0)
  1338. {
  1339. return format_number(format_[0], number);
  1340. }
  1341. else
  1342. {
  1343. return format_number(format_[1], std::fabs(number));
  1344. }
  1345. }
  1346. // 3+ sections, first for positive, second for negative, third for zero
  1347. else
  1348. {
  1349. if (number > 0)
  1350. {
  1351. return format_number(format_[0], number);
  1352. }
  1353. else if (number < 0)
  1354. {
  1355. return format_number(format_[1], std::fabs(number));
  1356. }
  1357. else
  1358. {
  1359. return format_number(format_[2], number);
  1360. }
  1361. }
  1362. }
  1363. std::string number_formatter::format_text(const std::string &text)
  1364. {
  1365. if (format_.size() < 4)
  1366. {
  1367. format_code temp;
  1368. template_part temp_part;
  1369. temp_part.type = template_part::template_type::general;
  1370. temp_part.placeholders.type = format_placeholders::placeholders_type::general;
  1371. temp.parts.push_back(temp_part);
  1372. return format_text(temp, text);
  1373. }
  1374. return format_text(format_[3], text);
  1375. }
  1376. std::string number_formatter::fill_placeholders(const format_placeholders &p, double number)
  1377. {
  1378. std::string result;
  1379. if (p.type == format_placeholders::placeholders_type::general
  1380. || p.type == format_placeholders::placeholders_type::text)
  1381. {
  1382. auto s = serialiser_.serialise_short(number);
  1383. while (s.size() > 1 && s.back() == '0')
  1384. {
  1385. s.pop_back();
  1386. }
  1387. if (s.back() == '.')
  1388. {
  1389. s.pop_back();
  1390. }
  1391. return s;
  1392. }
  1393. if (p.percentage)
  1394. {
  1395. number *= 100;
  1396. }
  1397. if (p.thousands_scale > 0)
  1398. {
  1399. number /= std::pow(1000.0, p.thousands_scale);
  1400. }
  1401. auto integer_part = static_cast<int>(number);
  1402. if (p.type == format_placeholders::placeholders_type::integer_only
  1403. || p.type == format_placeholders::placeholders_type::integer_part
  1404. || p.type == format_placeholders::placeholders_type::fraction_integer)
  1405. {
  1406. result = std::to_string(integer_part);
  1407. while (result.size() < p.num_zeros)
  1408. {
  1409. result = "0" + result;
  1410. }
  1411. while (result.size() < p.num_zeros + p.num_spaces)
  1412. {
  1413. result = " " + result;
  1414. }
  1415. if (p.use_comma_separator)
  1416. {
  1417. std::vector<char> digits(result.rbegin(), result.rend());
  1418. std::string temp;
  1419. for (std::size_t i = 0; i < digits.size(); i++)
  1420. {
  1421. temp.push_back(digits[i]);
  1422. if (i % 3 == 2)
  1423. {
  1424. temp.push_back(',');
  1425. }
  1426. }
  1427. result = std::string(temp.rbegin(), temp.rend());
  1428. }
  1429. if (p.percentage && p.type == format_placeholders::placeholders_type::integer_only)
  1430. {
  1431. result.push_back('%');
  1432. }
  1433. }
  1434. else if (p.type == format_placeholders::placeholders_type::fractional_part)
  1435. {
  1436. auto fractional_part = number - integer_part;
  1437. result = std::fabs(fractional_part) < std::numeric_limits<double>::min()
  1438. ? std::string(".")
  1439. : serialiser_.serialise_short(fractional_part).substr(1);
  1440. while (result.back() == '0' || result.size() > (p.num_zeros + p.num_optionals + p.num_spaces + 1))
  1441. {
  1442. result.pop_back();
  1443. }
  1444. if (result.size() < p.num_zeros + 1)
  1445. {
  1446. result.resize(p.num_zeros + 1, '0');
  1447. }
  1448. if (result.size() < p.num_zeros + p.num_optionals + p.num_spaces + 1)
  1449. {
  1450. result.resize(p.num_zeros + p.num_optionals + p.num_spaces + 1, ' ');
  1451. }
  1452. if (p.percentage)
  1453. {
  1454. result.push_back('%');
  1455. }
  1456. }
  1457. return result;
  1458. }
  1459. std::string number_formatter::fill_scientific_placeholders(const format_placeholders &integer_part,
  1460. const format_placeholders &fractional_part, const format_placeholders &exponent_part, double number)
  1461. {
  1462. std::size_t logarithm = 0;
  1463. if (number != 0.0)
  1464. {
  1465. logarithm = static_cast<std::size_t>(std::log10(number));
  1466. if (integer_part.num_zeros + integer_part.num_optionals > 1)
  1467. {
  1468. logarithm = integer_part.num_zeros + integer_part.num_optionals;
  1469. }
  1470. }
  1471. number /= std::pow(10.0, logarithm);
  1472. auto integer = static_cast<int>(number);
  1473. auto fraction = number - integer;
  1474. std::string integer_string = std::to_string(integer);
  1475. if (number == 0.0)
  1476. {
  1477. integer_string = std::string(integer_part.num_zeros + integer_part.num_optionals, '0');
  1478. }
  1479. std::string fractional_string = serialiser_.serialise_short(fraction).substr(1, fractional_part.num_zeros + fractional_part.num_optionals + 1);
  1480. std::string exponent_string = std::to_string(logarithm);
  1481. while (exponent_string.size() < fractional_part.num_zeros)
  1482. {
  1483. exponent_string.insert(0, "0");
  1484. }
  1485. if (exponent_part.type == format_placeholders::placeholders_type::scientific_exponent_plus)
  1486. {
  1487. exponent_string.insert(0, "E+");
  1488. }
  1489. else
  1490. {
  1491. exponent_string.insert(0, "E");
  1492. }
  1493. return integer_string + fractional_string + exponent_string;
  1494. }
  1495. std::string number_formatter::fill_fraction_placeholders(const format_placeholders & /*numerator*/,
  1496. const format_placeholders &denominator, double number, bool /*improper*/)
  1497. {
  1498. auto fractional_part = number - static_cast<int>(number);
  1499. auto original_fractional_part = fractional_part;
  1500. fractional_part *= 10;
  1501. while (std::abs(fractional_part - static_cast<int>(fractional_part)) > 0.000001
  1502. && std::abs(fractional_part - static_cast<int>(fractional_part)) < 0.999999)
  1503. {
  1504. fractional_part *= 10;
  1505. }
  1506. fractional_part = static_cast<int>(fractional_part);
  1507. auto denominator_digits = denominator.num_zeros + denominator.num_optionals + denominator.num_spaces;
  1508. // auto denominator_digits = static_cast<std::size_t>(std::ceil(std::log10(fractional_part)));
  1509. auto lower = static_cast<int>(std::pow(10, denominator_digits - 1));
  1510. auto upper = static_cast<int>(std::pow(10, denominator_digits));
  1511. auto best_denominator = lower;
  1512. auto best_difference = 1000.0;
  1513. for (int i = lower; i < upper; ++i)
  1514. {
  1515. auto numerator_full = original_fractional_part * i;
  1516. auto numerator_rounded = static_cast<int>(std::round(numerator_full));
  1517. auto difference = std::fabs(original_fractional_part - (numerator_rounded / static_cast<double>(i)));
  1518. if (difference < best_difference)
  1519. {
  1520. best_difference = difference;
  1521. best_denominator = i;
  1522. }
  1523. }
  1524. auto numerator_rounded = static_cast<int>(std::round(original_fractional_part * best_denominator));
  1525. return std::to_string(numerator_rounded) + "/" + std::to_string(best_denominator);
  1526. }
  1527. std::string number_formatter::format_number(const format_code &format, double number)
  1528. {
  1529. static const std::vector<std::string> month_names = std::vector<std::string>{"January", "February", "March",
  1530. "April", "May", "June", "July", "August", "September", "October", "November", "December"};
  1531. static const std::vector<std::string> day_names =
  1532. std::vector<std::string>{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
  1533. std::string result;
  1534. if (number < 0)
  1535. {
  1536. result.push_back('-');
  1537. if (format.is_datetime)
  1538. {
  1539. return std::string(11, '#');
  1540. }
  1541. }
  1542. number = std::fabs(number);
  1543. xlnt::datetime dt(0, 1, 0);
  1544. std::size_t hour = 0;
  1545. if (format.is_datetime)
  1546. {
  1547. if (number != 0.0)
  1548. {
  1549. dt = xlnt::datetime::from_number(number, calendar_);
  1550. }
  1551. hour = static_cast<std::size_t>(dt.hour);
  1552. if (format.twelve_hour)
  1553. {
  1554. hour %= 12;
  1555. if (hour == 0)
  1556. {
  1557. hour = 12;
  1558. }
  1559. }
  1560. }
  1561. bool improper_fraction = true;
  1562. std::size_t fill_index = 0;
  1563. bool fill = false;
  1564. std::string fill_character;
  1565. for (std::size_t i = 0; i < format.parts.size(); ++i)
  1566. {
  1567. const auto &part = format.parts[i];
  1568. switch (part.type)
  1569. {
  1570. case template_part::template_type::space: {
  1571. result.push_back(' ');
  1572. break;
  1573. }
  1574. case template_part::template_type::text: {
  1575. result.append(part.string);
  1576. break;
  1577. }
  1578. case template_part::template_type::fill: {
  1579. fill = true;
  1580. fill_index = result.size();
  1581. fill_character = part.string;
  1582. break;
  1583. }
  1584. case template_part::template_type::general: {
  1585. if (part.placeholders.type == format_placeholders::placeholders_type::fractional_part
  1586. && (format.is_datetime || format.is_timedelta))
  1587. {
  1588. auto digits = std::min(
  1589. static_cast<std::size_t>(6), part.placeholders.num_zeros + part.placeholders.num_optionals);
  1590. auto denominator = static_cast<int>(std::pow(10.0, digits));
  1591. auto fractional_seconds = dt.microsecond / 1.0E6 * denominator;
  1592. fractional_seconds = std::round(fractional_seconds) / denominator;
  1593. result.append(fill_placeholders(part.placeholders, fractional_seconds));
  1594. break;
  1595. }
  1596. if (part.placeholders.type == format_placeholders::placeholders_type::fraction_integer)
  1597. {
  1598. improper_fraction = false;
  1599. }
  1600. if (part.placeholders.type == format_placeholders::placeholders_type::fraction_numerator)
  1601. {
  1602. i += 2;
  1603. if (number == 0.0)
  1604. {
  1605. result.pop_back();
  1606. break;
  1607. }
  1608. result.append(fill_fraction_placeholders(
  1609. part.placeholders, format.parts[i].placeholders, number, improper_fraction));
  1610. }
  1611. else if (part.placeholders.scientific
  1612. && part.placeholders.type == format_placeholders::placeholders_type::integer_part)
  1613. {
  1614. auto integer_part = part.placeholders;
  1615. ++i;
  1616. auto fractional_part = format.parts[i++].placeholders;
  1617. auto exponent_part = format.parts[i++].placeholders;
  1618. result.append(fill_scientific_placeholders(integer_part, fractional_part, exponent_part, number));
  1619. }
  1620. else
  1621. {
  1622. result.append(fill_placeholders(part.placeholders, number));
  1623. }
  1624. break;
  1625. }
  1626. case template_part::template_type::day_number: {
  1627. result.append(std::to_string(dt.day));
  1628. break;
  1629. }
  1630. case template_part::template_type::day_number_leading_zero: {
  1631. if (dt.day < 10)
  1632. {
  1633. result.push_back('0');
  1634. }
  1635. result.append(std::to_string(dt.day));
  1636. break;
  1637. }
  1638. case template_part::template_type::month_abbreviation: {
  1639. result.append(month_names.at(static_cast<std::size_t>(dt.month) - 1).substr(0, 3));
  1640. break;
  1641. }
  1642. case template_part::template_type::month_name: {
  1643. result.append(month_names.at(static_cast<std::size_t>(dt.month) - 1));
  1644. break;
  1645. }
  1646. case template_part::template_type::month_number: {
  1647. result.append(std::to_string(dt.month));
  1648. break;
  1649. }
  1650. case template_part::template_type::month_number_leading_zero: {
  1651. if (dt.month < 10)
  1652. {
  1653. result.push_back('0');
  1654. }
  1655. result.append(std::to_string(dt.month));
  1656. break;
  1657. }
  1658. case template_part::template_type::year_short: {
  1659. if (dt.year % 1000 < 10)
  1660. {
  1661. result.push_back('0');
  1662. }
  1663. result.append(std::to_string(dt.year % 1000));
  1664. break;
  1665. }
  1666. case template_part::template_type::year_long: {
  1667. result.append(std::to_string(dt.year));
  1668. break;
  1669. }
  1670. case template_part::template_type::hour: {
  1671. result.append(std::to_string(hour));
  1672. break;
  1673. }
  1674. case template_part::template_type::hour_leading_zero: {
  1675. if (hour < 10)
  1676. {
  1677. result.push_back('0');
  1678. }
  1679. result.append(std::to_string(hour));
  1680. break;
  1681. }
  1682. case template_part::template_type::minute: {
  1683. result.append(std::to_string(dt.minute));
  1684. break;
  1685. }
  1686. case template_part::template_type::minute_leading_zero: {
  1687. if (dt.minute < 10)
  1688. {
  1689. result.push_back('0');
  1690. }
  1691. result.append(std::to_string(dt.minute));
  1692. break;
  1693. }
  1694. case template_part::template_type::second: {
  1695. result.append(std::to_string(dt.second + (dt.microsecond > 500000 ? 1 : 0)));
  1696. break;
  1697. }
  1698. case template_part::template_type::second_fractional: {
  1699. result.append(std::to_string(dt.second));
  1700. break;
  1701. }
  1702. case template_part::template_type::second_leading_zero: {
  1703. if ((dt.second + (dt.microsecond > 500000 ? 1 : 0)) < 10)
  1704. {
  1705. result.push_back('0');
  1706. }
  1707. result.append(std::to_string(dt.second + (dt.microsecond > 500000 ? 1 : 0)));
  1708. break;
  1709. }
  1710. case template_part::template_type::second_leading_zero_fractional: {
  1711. if (dt.second < 10)
  1712. {
  1713. result.push_back('0');
  1714. }
  1715. result.append(std::to_string(dt.second));
  1716. break;
  1717. }
  1718. case template_part::template_type::am_pm: {
  1719. if (dt.hour < 12)
  1720. {
  1721. result.append("AM");
  1722. }
  1723. else
  1724. {
  1725. result.append("PM");
  1726. }
  1727. break;
  1728. }
  1729. case template_part::template_type::a_p: {
  1730. if (dt.hour < 12)
  1731. {
  1732. result.append("A");
  1733. }
  1734. else
  1735. {
  1736. result.append("P");
  1737. }
  1738. break;
  1739. }
  1740. case template_part::template_type::elapsed_hours: {
  1741. result.append(std::to_string(24 * static_cast<int>(number) + dt.hour));
  1742. break;
  1743. }
  1744. case template_part::template_type::elapsed_minutes: {
  1745. result.append(std::to_string(24 * 60 * static_cast<int>(number)
  1746. + (60 * dt.hour) + dt.minute));
  1747. break;
  1748. }
  1749. case template_part::template_type::elapsed_seconds: {
  1750. result.append(std::to_string(24 * 60 * 60 * static_cast<int>(number)
  1751. + (60 * 60 * dt.hour) + (60 * dt.minute) + dt.second));
  1752. break;
  1753. }
  1754. case template_part::template_type::month_letter: {
  1755. result.append(month_names.at(static_cast<std::size_t>(dt.month) - 1).substr(0, 1));
  1756. break;
  1757. }
  1758. case template_part::template_type::day_abbreviation: {
  1759. result.append(day_names.at(static_cast<std::size_t>(dt.weekday())).substr(0, 3));
  1760. break;
  1761. }
  1762. case template_part::template_type::day_name: {
  1763. result.append(day_names.at(static_cast<std::size_t>(dt.weekday())));
  1764. break;
  1765. }
  1766. }
  1767. }
  1768. const std::size_t width = 11;
  1769. if (fill && result.size() < width)
  1770. {
  1771. auto remaining = width - result.size();
  1772. std::string fill_string(remaining, fill_character.front());
  1773. // TODO: A UTF-8 character could be multiple bytes
  1774. result = result.substr(0, fill_index) + fill_string + result.substr(fill_index);
  1775. }
  1776. return result;
  1777. }
  1778. std::string number_formatter::format_text(const format_code &format, const std::string &text)
  1779. {
  1780. std::string result;
  1781. bool any_text_part = false;
  1782. for (const auto &part : format.parts)
  1783. {
  1784. if (part.type == template_part::template_type::text)
  1785. {
  1786. result.append(part.string);
  1787. any_text_part = true;
  1788. }
  1789. else if (part.type == template_part::template_type::general)
  1790. {
  1791. if (part.placeholders.type == format_placeholders::placeholders_type::general
  1792. || part.placeholders.type == format_placeholders::placeholders_type::text)
  1793. {
  1794. result.append(text);
  1795. any_text_part = true;
  1796. }
  1797. }
  1798. }
  1799. if (!format.parts.empty() && !any_text_part)
  1800. {
  1801. return text;
  1802. }
  1803. return result;
  1804. }
  1805. } // namespace detail
  1806. } // namespace xlnt