differential code coverage report with master
Current view: top level - source/detail/number_format - number_formatter.cpp (source / functions) Coverage Total Hit UBC CBC
Current: coverage.info Lines: 83.6 % 987 825 162 825
Current Date: 2025-12-07 02:01:22 Functions: 100.0 % 23 23 23
Baseline: coverage_master.info Branches: 90.9 % 1413 1284 258 2568
Baseline Date: 2025-12-07 02:01:21

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

Generated by: LCOV version 2.3.1-beta