| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949 | 
							- <?php
 
- namespace PhpOffice\PhpSpreadsheet\Reader;
 
- use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 
- use PhpOffice\PhpSpreadsheet\Cell\DataType;
 
- use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
 
- use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
 
- use PhpOffice\PhpSpreadsheet\NamedRange;
 
- use PhpOffice\PhpSpreadsheet\RichText\RichText;
 
- use PhpOffice\PhpSpreadsheet\Shared\CodePage;
 
- use PhpOffice\PhpSpreadsheet\Shared\Date;
 
- use PhpOffice\PhpSpreadsheet\Shared\Escher;
 
- use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE;
 
- use PhpOffice\PhpSpreadsheet\Shared\File;
 
- use PhpOffice\PhpSpreadsheet\Shared\OLE;
 
- use PhpOffice\PhpSpreadsheet\Shared\OLERead;
 
- use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 
- use PhpOffice\PhpSpreadsheet\Spreadsheet;
 
- use PhpOffice\PhpSpreadsheet\Style\Alignment;
 
- use PhpOffice\PhpSpreadsheet\Style\Borders;
 
- use PhpOffice\PhpSpreadsheet\Style\Font;
 
- use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
 
- use PhpOffice\PhpSpreadsheet\Style\Protection;
 
- use PhpOffice\PhpSpreadsheet\Style\Style;
 
- use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
 
- use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
 
- use PhpOffice\PhpSpreadsheet\Worksheet\SheetView;
 
- use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 
- // Original file header of ParseXL (used as the base for this class):
 
- // --------------------------------------------------------------------------------
 
- // Adapted from Excel_Spreadsheet_Reader developed by users bizon153,
 
- // trex005, and mmp11 (SourceForge.net)
 
- // https://sourceforge.net/projects/phpexcelreader/
 
- // Primary changes made by canyoncasa (dvc) for ParseXL 1.00 ...
 
- //     Modelled moreso after Perl Excel Parse/Write modules
 
- //     Added Parse_Excel_Spreadsheet object
 
- //         Reads a whole worksheet or tab as row,column array or as
 
- //         associated hash of indexed rows and named column fields
 
- //     Added variables for worksheet (tab) indexes and names
 
- //     Added an object call for loading individual woorksheets
 
- //     Changed default indexing defaults to 0 based arrays
 
- //     Fixed date/time and percent formats
 
- //     Includes patches found at SourceForge...
 
- //         unicode patch by nobody
 
- //         unpack("d") machine depedency patch by matchy
 
- //         boundsheet utf16 patch by bjaenichen
 
- //     Renamed functions for shorter names
 
- //     General code cleanup and rigor, including <80 column width
 
- //     Included a testcase Excel file and PHP example calls
 
- //     Code works for PHP 5.x
 
- // Primary changes made by canyoncasa (dvc) for ParseXL 1.10 ...
 
- // http://sourceforge.net/tracker/index.php?func=detail&aid=1466964&group_id=99160&atid=623334
 
- //     Decoding of formula conditions, results, and tokens.
 
- //     Support for user-defined named cells added as an array "namedcells"
 
- //         Patch code for user-defined named cells supports single cells only.
 
- //         NOTE: this patch only works for BIFF8 as BIFF5-7 use a different
 
- //         external sheet reference structure
 
- class Xls extends BaseReader
 
- {
 
-     // ParseXL definitions
 
-     const XLS_BIFF8 = 0x0600;
 
-     const XLS_BIFF7 = 0x0500;
 
-     const XLS_WORKBOOKGLOBALS = 0x0005;
 
-     const XLS_WORKSHEET = 0x0010;
 
-     // record identifiers
 
-     const XLS_TYPE_FORMULA = 0x0006;
 
-     const XLS_TYPE_EOF = 0x000a;
 
-     const XLS_TYPE_PROTECT = 0x0012;
 
-     const XLS_TYPE_OBJECTPROTECT = 0x0063;
 
-     const XLS_TYPE_SCENPROTECT = 0x00dd;
 
-     const XLS_TYPE_PASSWORD = 0x0013;
 
-     const XLS_TYPE_HEADER = 0x0014;
 
-     const XLS_TYPE_FOOTER = 0x0015;
 
-     const XLS_TYPE_EXTERNSHEET = 0x0017;
 
-     const XLS_TYPE_DEFINEDNAME = 0x0018;
 
-     const XLS_TYPE_VERTICALPAGEBREAKS = 0x001a;
 
-     const XLS_TYPE_HORIZONTALPAGEBREAKS = 0x001b;
 
-     const XLS_TYPE_NOTE = 0x001c;
 
-     const XLS_TYPE_SELECTION = 0x001d;
 
-     const XLS_TYPE_DATEMODE = 0x0022;
 
-     const XLS_TYPE_EXTERNNAME = 0x0023;
 
-     const XLS_TYPE_LEFTMARGIN = 0x0026;
 
-     const XLS_TYPE_RIGHTMARGIN = 0x0027;
 
-     const XLS_TYPE_TOPMARGIN = 0x0028;
 
-     const XLS_TYPE_BOTTOMMARGIN = 0x0029;
 
-     const XLS_TYPE_PRINTGRIDLINES = 0x002b;
 
-     const XLS_TYPE_FILEPASS = 0x002f;
 
-     const XLS_TYPE_FONT = 0x0031;
 
-     const XLS_TYPE_CONTINUE = 0x003c;
 
-     const XLS_TYPE_PANE = 0x0041;
 
-     const XLS_TYPE_CODEPAGE = 0x0042;
 
-     const XLS_TYPE_DEFCOLWIDTH = 0x0055;
 
-     const XLS_TYPE_OBJ = 0x005d;
 
-     const XLS_TYPE_COLINFO = 0x007d;
 
-     const XLS_TYPE_IMDATA = 0x007f;
 
-     const XLS_TYPE_SHEETPR = 0x0081;
 
-     const XLS_TYPE_HCENTER = 0x0083;
 
-     const XLS_TYPE_VCENTER = 0x0084;
 
-     const XLS_TYPE_SHEET = 0x0085;
 
-     const XLS_TYPE_PALETTE = 0x0092;
 
-     const XLS_TYPE_SCL = 0x00a0;
 
-     const XLS_TYPE_PAGESETUP = 0x00a1;
 
-     const XLS_TYPE_MULRK = 0x00bd;
 
-     const XLS_TYPE_MULBLANK = 0x00be;
 
-     const XLS_TYPE_DBCELL = 0x00d7;
 
-     const XLS_TYPE_XF = 0x00e0;
 
-     const XLS_TYPE_MERGEDCELLS = 0x00e5;
 
-     const XLS_TYPE_MSODRAWINGGROUP = 0x00eb;
 
-     const XLS_TYPE_MSODRAWING = 0x00ec;
 
-     const XLS_TYPE_SST = 0x00fc;
 
-     const XLS_TYPE_LABELSST = 0x00fd;
 
-     const XLS_TYPE_EXTSST = 0x00ff;
 
-     const XLS_TYPE_EXTERNALBOOK = 0x01ae;
 
-     const XLS_TYPE_DATAVALIDATIONS = 0x01b2;
 
-     const XLS_TYPE_TXO = 0x01b6;
 
-     const XLS_TYPE_HYPERLINK = 0x01b8;
 
-     const XLS_TYPE_DATAVALIDATION = 0x01be;
 
-     const XLS_TYPE_DIMENSION = 0x0200;
 
-     const XLS_TYPE_BLANK = 0x0201;
 
-     const XLS_TYPE_NUMBER = 0x0203;
 
-     const XLS_TYPE_LABEL = 0x0204;
 
-     const XLS_TYPE_BOOLERR = 0x0205;
 
-     const XLS_TYPE_STRING = 0x0207;
 
-     const XLS_TYPE_ROW = 0x0208;
 
-     const XLS_TYPE_INDEX = 0x020b;
 
-     const XLS_TYPE_ARRAY = 0x0221;
 
-     const XLS_TYPE_DEFAULTROWHEIGHT = 0x0225;
 
-     const XLS_TYPE_WINDOW2 = 0x023e;
 
-     const XLS_TYPE_RK = 0x027e;
 
-     const XLS_TYPE_STYLE = 0x0293;
 
-     const XLS_TYPE_FORMAT = 0x041e;
 
-     const XLS_TYPE_SHAREDFMLA = 0x04bc;
 
-     const XLS_TYPE_BOF = 0x0809;
 
-     const XLS_TYPE_SHEETPROTECTION = 0x0867;
 
-     const XLS_TYPE_RANGEPROTECTION = 0x0868;
 
-     const XLS_TYPE_SHEETLAYOUT = 0x0862;
 
-     const XLS_TYPE_XFEXT = 0x087d;
 
-     const XLS_TYPE_PAGELAYOUTVIEW = 0x088b;
 
-     const XLS_TYPE_UNKNOWN = 0xffff;
 
-     // Encryption type
 
-     const MS_BIFF_CRYPTO_NONE = 0;
 
-     const MS_BIFF_CRYPTO_XOR = 1;
 
-     const MS_BIFF_CRYPTO_RC4 = 2;
 
-     // Size of stream blocks when using RC4 encryption
 
-     const REKEY_BLOCK = 0x400;
 
-     /**
 
-      * Summary Information stream data.
 
-      *
 
-      * @var string
 
-      */
 
-     private $summaryInformation;
 
-     /**
 
-      * Extended Summary Information stream data.
 
-      *
 
-      * @var string
 
-      */
 
-     private $documentSummaryInformation;
 
-     /**
 
-      * Workbook stream data. (Includes workbook globals substream as well as sheet substreams).
 
-      *
 
-      * @var string
 
-      */
 
-     private $data;
 
-     /**
 
-      * Size in bytes of $this->data.
 
-      *
 
-      * @var int
 
-      */
 
-     private $dataSize;
 
-     /**
 
-      * Current position in stream.
 
-      *
 
-      * @var int
 
-      */
 
-     private $pos;
 
-     /**
 
-      * Workbook to be returned by the reader.
 
-      *
 
-      * @var Spreadsheet
 
-      */
 
-     private $spreadsheet;
 
-     /**
 
-      * Worksheet that is currently being built by the reader.
 
-      *
 
-      * @var Worksheet
 
-      */
 
-     private $phpSheet;
 
-     /**
 
-      * BIFF version.
 
-      *
 
-      * @var int
 
-      */
 
-     private $version;
 
-     /**
 
-      * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
 
-      * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'.
 
-      *
 
-      * @var string
 
-      */
 
-     private $codepage;
 
-     /**
 
-      * Shared formats.
 
-      *
 
-      * @var array
 
-      */
 
-     private $formats;
 
-     /**
 
-      * Shared fonts.
 
-      *
 
-      * @var array
 
-      */
 
-     private $objFonts;
 
-     /**
 
-      * Color palette.
 
-      *
 
-      * @var array
 
-      */
 
-     private $palette;
 
-     /**
 
-      * Worksheets.
 
-      *
 
-      * @var array
 
-      */
 
-     private $sheets;
 
-     /**
 
-      * External books.
 
-      *
 
-      * @var array
 
-      */
 
-     private $externalBooks;
 
-     /**
 
-      * REF structures. Only applies to BIFF8.
 
-      *
 
-      * @var array
 
-      */
 
-     private $ref;
 
-     /**
 
-      * External names.
 
-      *
 
-      * @var array
 
-      */
 
-     private $externalNames;
 
-     /**
 
-      * Defined names.
 
-      *
 
-      * @var array
 
-      */
 
-     private $definedname;
 
-     /**
 
-      * Shared strings. Only applies to BIFF8.
 
-      *
 
-      * @var array
 
-      */
 
-     private $sst;
 
-     /**
 
-      * Panes are frozen? (in sheet currently being read). See WINDOW2 record.
 
-      *
 
-      * @var bool
 
-      */
 
-     private $frozen;
 
-     /**
 
-      * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record.
 
-      *
 
-      * @var bool
 
-      */
 
-     private $isFitToPages;
 
-     /**
 
-      * Objects. One OBJ record contributes with one entry.
 
-      *
 
-      * @var array
 
-      */
 
-     private $objs;
 
-     /**
 
-      * Text Objects. One TXO record corresponds with one entry.
 
-      *
 
-      * @var array
 
-      */
 
-     private $textObjects;
 
-     /**
 
-      * Cell Annotations (BIFF8).
 
-      *
 
-      * @var array
 
-      */
 
-     private $cellNotes;
 
-     /**
 
-      * The combined MSODRAWINGGROUP data.
 
-      *
 
-      * @var string
 
-      */
 
-     private $drawingGroupData;
 
-     /**
 
-      * The combined MSODRAWING data (per sheet).
 
-      *
 
-      * @var string
 
-      */
 
-     private $drawingData;
 
-     /**
 
-      * Keep track of XF index.
 
-      *
 
-      * @var int
 
-      */
 
-     private $xfIndex;
 
-     /**
 
-      * Mapping of XF index (that is a cell XF) to final index in cellXf collection.
 
-      *
 
-      * @var array
 
-      */
 
-     private $mapCellXfIndex;
 
-     /**
 
-      * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection.
 
-      *
 
-      * @var array
 
-      */
 
-     private $mapCellStyleXfIndex;
 
-     /**
 
-      * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value.
 
-      *
 
-      * @var array
 
-      */
 
-     private $sharedFormulas;
 
-     /**
 
-      * The shared formula parts in a sheet. One FORMULA record contributes with one value if it
 
-      * refers to a shared formula.
 
-      *
 
-      * @var array
 
-      */
 
-     private $sharedFormulaParts;
 
-     /**
 
-      * The type of encryption in use.
 
-      *
 
-      * @var int
 
-      */
 
-     private $encryption = 0;
 
-     /**
 
-      * The position in the stream after which contents are encrypted.
 
-      *
 
-      * @var int
 
-      */
 
-     private $encryptionStartPos = false;
 
-     /**
 
-      * The current RC4 decryption object.
 
-      *
 
-      * @var Xls\RC4
 
-      */
 
-     private $rc4Key;
 
-     /**
 
-      * The position in the stream that the RC4 decryption object was left at.
 
-      *
 
-      * @var int
 
-      */
 
-     private $rc4Pos = 0;
 
-     /**
 
-      * The current MD5 context state.
 
-      *
 
-      * @var string
 
-      */
 
-     private $md5Ctxt;
 
-     /**
 
-      * @var int
 
-      */
 
-     private $textObjRef;
 
-     /**
 
-      * @var string
 
-      */
 
-     private $baseCell;
 
-     /**
 
-      * Create a new Xls Reader instance.
 
-      */
 
-     public function __construct()
 
-     {
 
-         $this->readFilter = new DefaultReadFilter();
 
-     }
 
-     /**
 
-      * Can the current IReader read the file?
 
-      *
 
-      * @param string $pFilename
 
-      *
 
-      * @return bool
 
-      */
 
-     public function canRead($pFilename)
 
-     {
 
-         File::assertFile($pFilename);
 
-         try {
 
-             // Use ParseXL for the hard work.
 
-             $ole = new OLERead();
 
-             // get excel data
 
-             $ole->read($pFilename);
 
-             return true;
 
-         } catch (PhpSpreadsheetException $e) {
 
-             return false;
 
-         }
 
-     }
 
-     /**
 
-      * Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object.
 
-      *
 
-      * @param string $pFilename
 
-      *
 
-      * @throws Exception
 
-      *
 
-      * @return array
 
-      */
 
-     public function listWorksheetNames($pFilename)
 
-     {
 
-         File::assertFile($pFilename);
 
-         $worksheetNames = [];
 
-         // Read the OLE file
 
-         $this->loadOLE($pFilename);
 
-         // total byte size of Excel data (workbook global substream + sheet substreams)
 
-         $this->dataSize = strlen($this->data);
 
-         $this->pos = 0;
 
-         $this->sheets = [];
 
-         // Parse Workbook Global Substream
 
-         while ($this->pos < $this->dataSize) {
 
-             $code = self::getUInt2d($this->data, $this->pos);
 
-             switch ($code) {
 
-                 case self::XLS_TYPE_BOF:
 
-                     $this->readBof();
 
-                     break;
 
-                 case self::XLS_TYPE_SHEET:
 
-                     $this->readSheet();
 
-                     break;
 
-                 case self::XLS_TYPE_EOF:
 
-                     $this->readDefault();
 
-                     break 2;
 
-                 default:
 
-                     $this->readDefault();
 
-                     break;
 
-             }
 
-         }
 
-         foreach ($this->sheets as $sheet) {
 
-             if ($sheet['sheetType'] != 0x00) {
 
-                 // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
 
-                 continue;
 
-             }
 
-             $worksheetNames[] = $sheet['name'];
 
-         }
 
-         return $worksheetNames;
 
-     }
 
-     /**
 
-      * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
 
-      *
 
-      * @param string $pFilename
 
-      *
 
-      * @throws Exception
 
-      *
 
-      * @return array
 
-      */
 
-     public function listWorksheetInfo($pFilename)
 
-     {
 
-         File::assertFile($pFilename);
 
-         $worksheetInfo = [];
 
-         // Read the OLE file
 
-         $this->loadOLE($pFilename);
 
-         // total byte size of Excel data (workbook global substream + sheet substreams)
 
-         $this->dataSize = strlen($this->data);
 
-         // initialize
 
-         $this->pos = 0;
 
-         $this->sheets = [];
 
-         // Parse Workbook Global Substream
 
-         while ($this->pos < $this->dataSize) {
 
-             $code = self::getUInt2d($this->data, $this->pos);
 
-             switch ($code) {
 
-                 case self::XLS_TYPE_BOF:
 
-                     $this->readBof();
 
-                     break;
 
-                 case self::XLS_TYPE_SHEET:
 
-                     $this->readSheet();
 
-                     break;
 
-                 case self::XLS_TYPE_EOF:
 
-                     $this->readDefault();
 
-                     break 2;
 
-                 default:
 
-                     $this->readDefault();
 
-                     break;
 
-             }
 
-         }
 
-         // Parse the individual sheets
 
-         foreach ($this->sheets as $sheet) {
 
-             if ($sheet['sheetType'] != 0x00) {
 
-                 // 0x00: Worksheet
 
-                 // 0x02: Chart
 
-                 // 0x06: Visual Basic module
 
-                 continue;
 
-             }
 
-             $tmpInfo = [];
 
-             $tmpInfo['worksheetName'] = $sheet['name'];
 
-             $tmpInfo['lastColumnLetter'] = 'A';
 
-             $tmpInfo['lastColumnIndex'] = 0;
 
-             $tmpInfo['totalRows'] = 0;
 
-             $tmpInfo['totalColumns'] = 0;
 
-             $this->pos = $sheet['offset'];
 
-             while ($this->pos <= $this->dataSize - 4) {
 
-                 $code = self::getUInt2d($this->data, $this->pos);
 
-                 switch ($code) {
 
-                     case self::XLS_TYPE_RK:
 
-                     case self::XLS_TYPE_LABELSST:
 
-                     case self::XLS_TYPE_NUMBER:
 
-                     case self::XLS_TYPE_FORMULA:
 
-                     case self::XLS_TYPE_BOOLERR:
 
-                     case self::XLS_TYPE_LABEL:
 
-                         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-                         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-                         // move stream pointer to next record
 
-                         $this->pos += 4 + $length;
 
-                         $rowIndex = self::getUInt2d($recordData, 0) + 1;
 
-                         $columnIndex = self::getUInt2d($recordData, 2);
 
-                         $tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex);
 
-                         $tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex);
 
-                         break;
 
-                     case self::XLS_TYPE_BOF:
 
-                         $this->readBof();
 
-                         break;
 
-                     case self::XLS_TYPE_EOF:
 
-                         $this->readDefault();
 
-                         break 2;
 
-                     default:
 
-                         $this->readDefault();
 
-                         break;
 
-                 }
 
-             }
 
-             $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
 
-             $tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1;
 
-             $worksheetInfo[] = $tmpInfo;
 
-         }
 
-         return $worksheetInfo;
 
-     }
 
-     /**
 
-      * Loads PhpSpreadsheet from file.
 
-      *
 
-      * @param string $pFilename
 
-      *
 
-      * @throws Exception
 
-      *
 
-      * @return Spreadsheet
 
-      */
 
-     public function load($pFilename)
 
-     {
 
-         // Read the OLE file
 
-         $this->loadOLE($pFilename);
 
-         // Initialisations
 
-         $this->spreadsheet = new Spreadsheet();
 
-         $this->spreadsheet->removeSheetByIndex(0); // remove 1st sheet
 
-         if (!$this->readDataOnly) {
 
-             $this->spreadsheet->removeCellStyleXfByIndex(0); // remove the default style
 
-             $this->spreadsheet->removeCellXfByIndex(0); // remove the default style
 
-         }
 
-         // Read the summary information stream (containing meta data)
 
-         $this->readSummaryInformation();
 
-         // Read the Additional document summary information stream (containing application-specific meta data)
 
-         $this->readDocumentSummaryInformation();
 
-         // total byte size of Excel data (workbook global substream + sheet substreams)
 
-         $this->dataSize = strlen($this->data);
 
-         // initialize
 
-         $this->pos = 0;
 
-         $this->codepage = 'CP1252';
 
-         $this->formats = [];
 
-         $this->objFonts = [];
 
-         $this->palette = [];
 
-         $this->sheets = [];
 
-         $this->externalBooks = [];
 
-         $this->ref = [];
 
-         $this->definedname = [];
 
-         $this->sst = [];
 
-         $this->drawingGroupData = '';
 
-         $this->xfIndex = '';
 
-         $this->mapCellXfIndex = [];
 
-         $this->mapCellStyleXfIndex = [];
 
-         // Parse Workbook Global Substream
 
-         while ($this->pos < $this->dataSize) {
 
-             $code = self::getUInt2d($this->data, $this->pos);
 
-             switch ($code) {
 
-                 case self::XLS_TYPE_BOF:
 
-                     $this->readBof();
 
-                     break;
 
-                 case self::XLS_TYPE_FILEPASS:
 
-                     $this->readFilepass();
 
-                     break;
 
-                 case self::XLS_TYPE_CODEPAGE:
 
-                     $this->readCodepage();
 
-                     break;
 
-                 case self::XLS_TYPE_DATEMODE:
 
-                     $this->readDateMode();
 
-                     break;
 
-                 case self::XLS_TYPE_FONT:
 
-                     $this->readFont();
 
-                     break;
 
-                 case self::XLS_TYPE_FORMAT:
 
-                     $this->readFormat();
 
-                     break;
 
-                 case self::XLS_TYPE_XF:
 
-                     $this->readXf();
 
-                     break;
 
-                 case self::XLS_TYPE_XFEXT:
 
-                     $this->readXfExt();
 
-                     break;
 
-                 case self::XLS_TYPE_STYLE:
 
-                     $this->readStyle();
 
-                     break;
 
-                 case self::XLS_TYPE_PALETTE:
 
-                     $this->readPalette();
 
-                     break;
 
-                 case self::XLS_TYPE_SHEET:
 
-                     $this->readSheet();
 
-                     break;
 
-                 case self::XLS_TYPE_EXTERNALBOOK:
 
-                     $this->readExternalBook();
 
-                     break;
 
-                 case self::XLS_TYPE_EXTERNNAME:
 
-                     $this->readExternName();
 
-                     break;
 
-                 case self::XLS_TYPE_EXTERNSHEET:
 
-                     $this->readExternSheet();
 
-                     break;
 
-                 case self::XLS_TYPE_DEFINEDNAME:
 
-                     $this->readDefinedName();
 
-                     break;
 
-                 case self::XLS_TYPE_MSODRAWINGGROUP:
 
-                     $this->readMsoDrawingGroup();
 
-                     break;
 
-                 case self::XLS_TYPE_SST:
 
-                     $this->readSst();
 
-                     break;
 
-                 case self::XLS_TYPE_EOF:
 
-                     $this->readDefault();
 
-                     break 2;
 
-                 default:
 
-                     $this->readDefault();
 
-                     break;
 
-             }
 
-         }
 
-         // Resolve indexed colors for font, fill, and border colors
 
-         // Cannot be resolved already in XF record, because PALETTE record comes afterwards
 
-         if (!$this->readDataOnly) {
 
-             foreach ($this->objFonts as $objFont) {
 
-                 if (isset($objFont->colorIndex)) {
 
-                     $color = Xls\Color::map($objFont->colorIndex, $this->palette, $this->version);
 
-                     $objFont->getColor()->setRGB($color['rgb']);
 
-                 }
 
-             }
 
-             foreach ($this->spreadsheet->getCellXfCollection() as $objStyle) {
 
-                 // fill start and end color
 
-                 $fill = $objStyle->getFill();
 
-                 if (isset($fill->startcolorIndex)) {
 
-                     $startColor = Xls\Color::map($fill->startcolorIndex, $this->palette, $this->version);
 
-                     $fill->getStartColor()->setRGB($startColor['rgb']);
 
-                 }
 
-                 if (isset($fill->endcolorIndex)) {
 
-                     $endColor = Xls\Color::map($fill->endcolorIndex, $this->palette, $this->version);
 
-                     $fill->getEndColor()->setRGB($endColor['rgb']);
 
-                 }
 
-                 // border colors
 
-                 $top = $objStyle->getBorders()->getTop();
 
-                 $right = $objStyle->getBorders()->getRight();
 
-                 $bottom = $objStyle->getBorders()->getBottom();
 
-                 $left = $objStyle->getBorders()->getLeft();
 
-                 $diagonal = $objStyle->getBorders()->getDiagonal();
 
-                 if (isset($top->colorIndex)) {
 
-                     $borderTopColor = Xls\Color::map($top->colorIndex, $this->palette, $this->version);
 
-                     $top->getColor()->setRGB($borderTopColor['rgb']);
 
-                 }
 
-                 if (isset($right->colorIndex)) {
 
-                     $borderRightColor = Xls\Color::map($right->colorIndex, $this->palette, $this->version);
 
-                     $right->getColor()->setRGB($borderRightColor['rgb']);
 
-                 }
 
-                 if (isset($bottom->colorIndex)) {
 
-                     $borderBottomColor = Xls\Color::map($bottom->colorIndex, $this->palette, $this->version);
 
-                     $bottom->getColor()->setRGB($borderBottomColor['rgb']);
 
-                 }
 
-                 if (isset($left->colorIndex)) {
 
-                     $borderLeftColor = Xls\Color::map($left->colorIndex, $this->palette, $this->version);
 
-                     $left->getColor()->setRGB($borderLeftColor['rgb']);
 
-                 }
 
-                 if (isset($diagonal->colorIndex)) {
 
-                     $borderDiagonalColor = Xls\Color::map($diagonal->colorIndex, $this->palette, $this->version);
 
-                     $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
 
-                 }
 
-             }
 
-         }
 
-         // treat MSODRAWINGGROUP records, workbook-level Escher
 
-         if (!$this->readDataOnly && $this->drawingGroupData) {
 
-             $escherWorkbook = new Escher();
 
-             $reader = new Xls\Escher($escherWorkbook);
 
-             $escherWorkbook = $reader->load($this->drawingGroupData);
 
-         }
 
-         // Parse the individual sheets
 
-         foreach ($this->sheets as $sheet) {
 
-             if ($sheet['sheetType'] != 0x00) {
 
-                 // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
 
-                 continue;
 
-             }
 
-             // check if sheet should be skipped
 
-             if (isset($this->loadSheetsOnly) && !in_array($sheet['name'], $this->loadSheetsOnly)) {
 
-                 continue;
 
-             }
 
-             // add sheet to PhpSpreadsheet object
 
-             $this->phpSheet = $this->spreadsheet->createSheet();
 
-             //    Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula
 
-             //        cells... during the load, all formulae should be correct, and we're simply bringing the worksheet
 
-             //        name in line with the formula, not the reverse
 
-             $this->phpSheet->setTitle($sheet['name'], false, false);
 
-             $this->phpSheet->setSheetState($sheet['sheetState']);
 
-             $this->pos = $sheet['offset'];
 
-             // Initialize isFitToPages. May change after reading SHEETPR record.
 
-             $this->isFitToPages = false;
 
-             // Initialize drawingData
 
-             $this->drawingData = '';
 
-             // Initialize objs
 
-             $this->objs = [];
 
-             // Initialize shared formula parts
 
-             $this->sharedFormulaParts = [];
 
-             // Initialize shared formulas
 
-             $this->sharedFormulas = [];
 
-             // Initialize text objs
 
-             $this->textObjects = [];
 
-             // Initialize cell annotations
 
-             $this->cellNotes = [];
 
-             $this->textObjRef = -1;
 
-             while ($this->pos <= $this->dataSize - 4) {
 
-                 $code = self::getUInt2d($this->data, $this->pos);
 
-                 switch ($code) {
 
-                     case self::XLS_TYPE_BOF:
 
-                         $this->readBof();
 
-                         break;
 
-                     case self::XLS_TYPE_PRINTGRIDLINES:
 
-                         $this->readPrintGridlines();
 
-                         break;
 
-                     case self::XLS_TYPE_DEFAULTROWHEIGHT:
 
-                         $this->readDefaultRowHeight();
 
-                         break;
 
-                     case self::XLS_TYPE_SHEETPR:
 
-                         $this->readSheetPr();
 
-                         break;
 
-                     case self::XLS_TYPE_HORIZONTALPAGEBREAKS:
 
-                         $this->readHorizontalPageBreaks();
 
-                         break;
 
-                     case self::XLS_TYPE_VERTICALPAGEBREAKS:
 
-                         $this->readVerticalPageBreaks();
 
-                         break;
 
-                     case self::XLS_TYPE_HEADER:
 
-                         $this->readHeader();
 
-                         break;
 
-                     case self::XLS_TYPE_FOOTER:
 
-                         $this->readFooter();
 
-                         break;
 
-                     case self::XLS_TYPE_HCENTER:
 
-                         $this->readHcenter();
 
-                         break;
 
-                     case self::XLS_TYPE_VCENTER:
 
-                         $this->readVcenter();
 
-                         break;
 
-                     case self::XLS_TYPE_LEFTMARGIN:
 
-                         $this->readLeftMargin();
 
-                         break;
 
-                     case self::XLS_TYPE_RIGHTMARGIN:
 
-                         $this->readRightMargin();
 
-                         break;
 
-                     case self::XLS_TYPE_TOPMARGIN:
 
-                         $this->readTopMargin();
 
-                         break;
 
-                     case self::XLS_TYPE_BOTTOMMARGIN:
 
-                         $this->readBottomMargin();
 
-                         break;
 
-                     case self::XLS_TYPE_PAGESETUP:
 
-                         $this->readPageSetup();
 
-                         break;
 
-                     case self::XLS_TYPE_PROTECT:
 
-                         $this->readProtect();
 
-                         break;
 
-                     case self::XLS_TYPE_SCENPROTECT:
 
-                         $this->readScenProtect();
 
-                         break;
 
-                     case self::XLS_TYPE_OBJECTPROTECT:
 
-                         $this->readObjectProtect();
 
-                         break;
 
-                     case self::XLS_TYPE_PASSWORD:
 
-                         $this->readPassword();
 
-                         break;
 
-                     case self::XLS_TYPE_DEFCOLWIDTH:
 
-                         $this->readDefColWidth();
 
-                         break;
 
-                     case self::XLS_TYPE_COLINFO:
 
-                         $this->readColInfo();
 
-                         break;
 
-                     case self::XLS_TYPE_DIMENSION:
 
-                         $this->readDefault();
 
-                         break;
 
-                     case self::XLS_TYPE_ROW:
 
-                         $this->readRow();
 
-                         break;
 
-                     case self::XLS_TYPE_DBCELL:
 
-                         $this->readDefault();
 
-                         break;
 
-                     case self::XLS_TYPE_RK:
 
-                         $this->readRk();
 
-                         break;
 
-                     case self::XLS_TYPE_LABELSST:
 
-                         $this->readLabelSst();
 
-                         break;
 
-                     case self::XLS_TYPE_MULRK:
 
-                         $this->readMulRk();
 
-                         break;
 
-                     case self::XLS_TYPE_NUMBER:
 
-                         $this->readNumber();
 
-                         break;
 
-                     case self::XLS_TYPE_FORMULA:
 
-                         $this->readFormula();
 
-                         break;
 
-                     case self::XLS_TYPE_SHAREDFMLA:
 
-                         $this->readSharedFmla();
 
-                         break;
 
-                     case self::XLS_TYPE_BOOLERR:
 
-                         $this->readBoolErr();
 
-                         break;
 
-                     case self::XLS_TYPE_MULBLANK:
 
-                         $this->readMulBlank();
 
-                         break;
 
-                     case self::XLS_TYPE_LABEL:
 
-                         $this->readLabel();
 
-                         break;
 
-                     case self::XLS_TYPE_BLANK:
 
-                         $this->readBlank();
 
-                         break;
 
-                     case self::XLS_TYPE_MSODRAWING:
 
-                         $this->readMsoDrawing();
 
-                         break;
 
-                     case self::XLS_TYPE_OBJ:
 
-                         $this->readObj();
 
-                         break;
 
-                     case self::XLS_TYPE_WINDOW2:
 
-                         $this->readWindow2();
 
-                         break;
 
-                     case self::XLS_TYPE_PAGELAYOUTVIEW:
 
-                         $this->readPageLayoutView();
 
-                         break;
 
-                     case self::XLS_TYPE_SCL:
 
-                         $this->readScl();
 
-                         break;
 
-                     case self::XLS_TYPE_PANE:
 
-                         $this->readPane();
 
-                         break;
 
-                     case self::XLS_TYPE_SELECTION:
 
-                         $this->readSelection();
 
-                         break;
 
-                     case self::XLS_TYPE_MERGEDCELLS:
 
-                         $this->readMergedCells();
 
-                         break;
 
-                     case self::XLS_TYPE_HYPERLINK:
 
-                         $this->readHyperLink();
 
-                         break;
 
-                     case self::XLS_TYPE_DATAVALIDATIONS:
 
-                         $this->readDataValidations();
 
-                         break;
 
-                     case self::XLS_TYPE_DATAVALIDATION:
 
-                         $this->readDataValidation();
 
-                         break;
 
-                     case self::XLS_TYPE_SHEETLAYOUT:
 
-                         $this->readSheetLayout();
 
-                         break;
 
-                     case self::XLS_TYPE_SHEETPROTECTION:
 
-                         $this->readSheetProtection();
 
-                         break;
 
-                     case self::XLS_TYPE_RANGEPROTECTION:
 
-                         $this->readRangeProtection();
 
-                         break;
 
-                     case self::XLS_TYPE_NOTE:
 
-                         $this->readNote();
 
-                         break;
 
-                     case self::XLS_TYPE_TXO:
 
-                         $this->readTextObject();
 
-                         break;
 
-                     case self::XLS_TYPE_CONTINUE:
 
-                         $this->readContinue();
 
-                         break;
 
-                     case self::XLS_TYPE_EOF:
 
-                         $this->readDefault();
 
-                         break 2;
 
-                     default:
 
-                         $this->readDefault();
 
-                         break;
 
-                 }
 
-             }
 
-             // treat MSODRAWING records, sheet-level Escher
 
-             if (!$this->readDataOnly && $this->drawingData) {
 
-                 $escherWorksheet = new Escher();
 
-                 $reader = new Xls\Escher($escherWorksheet);
 
-                 $escherWorksheet = $reader->load($this->drawingData);
 
-                 // get all spContainers in one long array, so they can be mapped to OBJ records
 
-                 $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
 
-             }
 
-             // treat OBJ records
 
-             foreach ($this->objs as $n => $obj) {
 
-                 // the first shape container never has a corresponding OBJ record, hence $n + 1
 
-                 if (isset($allSpContainers[$n + 1]) && is_object($allSpContainers[$n + 1])) {
 
-                     $spContainer = $allSpContainers[$n + 1];
 
-                     // we skip all spContainers that are a part of a group shape since we cannot yet handle those
 
-                     if ($spContainer->getNestingLevel() > 1) {
 
-                         continue;
 
-                     }
 
-                     // calculate the width and height of the shape
 
-                     list($startColumn, $startRow) = Coordinate::coordinateFromString($spContainer->getStartCoordinates());
 
-                     list($endColumn, $endRow) = Coordinate::coordinateFromString($spContainer->getEndCoordinates());
 
-                     $startOffsetX = $spContainer->getStartOffsetX();
 
-                     $startOffsetY = $spContainer->getStartOffsetY();
 
-                     $endOffsetX = $spContainer->getEndOffsetX();
 
-                     $endOffsetY = $spContainer->getEndOffsetY();
 
-                     $width = \PhpOffice\PhpSpreadsheet\Shared\Xls::getDistanceX($this->phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
 
-                     $height = \PhpOffice\PhpSpreadsheet\Shared\Xls::getDistanceY($this->phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
 
-                     // calculate offsetX and offsetY of the shape
 
-                     $offsetX = $startOffsetX * \PhpOffice\PhpSpreadsheet\Shared\Xls::sizeCol($this->phpSheet, $startColumn) / 1024;
 
-                     $offsetY = $startOffsetY * \PhpOffice\PhpSpreadsheet\Shared\Xls::sizeRow($this->phpSheet, $startRow) / 256;
 
-                     switch ($obj['otObjType']) {
 
-                         case 0x19:
 
-                             // Note
 
-                             if (isset($this->cellNotes[$obj['idObjID']])) {
 
-                                 $cellNote = $this->cellNotes[$obj['idObjID']];
 
-                                 if (isset($this->textObjects[$obj['idObjID']])) {
 
-                                     $textObject = $this->textObjects[$obj['idObjID']];
 
-                                     $this->cellNotes[$obj['idObjID']]['objTextData'] = $textObject;
 
-                                 }
 
-                             }
 
-                             break;
 
-                         case 0x08:
 
-                             // picture
 
-                             // get index to BSE entry (1-based)
 
-                             $BSEindex = $spContainer->getOPT(0x0104);
 
-                             // If there is no BSE Index, we will fail here and other fields are not read.
 
-                             // Fix by checking here.
 
-                             // TODO: Why is there no BSE Index? Is this a new Office Version? Password protected field?
 
-                             // More likely : a uncompatible picture
 
-                             if (!$BSEindex) {
 
-                                 continue 2;
 
-                             }
 
-                             $BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection();
 
-                             $BSE = $BSECollection[$BSEindex - 1];
 
-                             $blipType = $BSE->getBlipType();
 
-                             // need check because some blip types are not supported by Escher reader such as EMF
 
-                             if ($blip = $BSE->getBlip()) {
 
-                                 $ih = imagecreatefromstring($blip->getData());
 
-                                 $drawing = new MemoryDrawing();
 
-                                 $drawing->setImageResource($ih);
 
-                                 // width, height, offsetX, offsetY
 
-                                 $drawing->setResizeProportional(false);
 
-                                 $drawing->setWidth($width);
 
-                                 $drawing->setHeight($height);
 
-                                 $drawing->setOffsetX($offsetX);
 
-                                 $drawing->setOffsetY($offsetY);
 
-                                 switch ($blipType) {
 
-                                     case BSE::BLIPTYPE_JPEG:
 
-                                         $drawing->setRenderingFunction(MemoryDrawing::RENDERING_JPEG);
 
-                                         $drawing->setMimeType(MemoryDrawing::MIMETYPE_JPEG);
 
-                                         break;
 
-                                     case BSE::BLIPTYPE_PNG:
 
-                                         $drawing->setRenderingFunction(MemoryDrawing::RENDERING_PNG);
 
-                                         $drawing->setMimeType(MemoryDrawing::MIMETYPE_PNG);
 
-                                         break;
 
-                                 }
 
-                                 $drawing->setWorksheet($this->phpSheet);
 
-                                 $drawing->setCoordinates($spContainer->getStartCoordinates());
 
-                             }
 
-                             break;
 
-                         default:
 
-                             // other object type
 
-                             break;
 
-                     }
 
-                 }
 
-             }
 
-             // treat SHAREDFMLA records
 
-             if ($this->version == self::XLS_BIFF8) {
 
-                 foreach ($this->sharedFormulaParts as $cell => $baseCell) {
 
-                     list($column, $row) = Coordinate::coordinateFromString($cell);
 
-                     if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
 
-                         $formula = $this->getFormulaFromStructure($this->sharedFormulas[$baseCell], $cell);
 
-                         $this->phpSheet->getCell($cell)->setValueExplicit('=' . $formula, DataType::TYPE_FORMULA);
 
-                     }
 
-                 }
 
-             }
 
-             if (!empty($this->cellNotes)) {
 
-                 foreach ($this->cellNotes as $note => $noteDetails) {
 
-                     if (!isset($noteDetails['objTextData'])) {
 
-                         if (isset($this->textObjects[$note])) {
 
-                             $textObject = $this->textObjects[$note];
 
-                             $noteDetails['objTextData'] = $textObject;
 
-                         } else {
 
-                             $noteDetails['objTextData']['text'] = '';
 
-                         }
 
-                     }
 
-                     $cellAddress = str_replace('$', '', $noteDetails['cellRef']);
 
-                     $this->phpSheet->getComment($cellAddress)->setAuthor($noteDetails['author'])->setText($this->parseRichText($noteDetails['objTextData']['text']));
 
-                 }
 
-             }
 
-         }
 
-         // add the named ranges (defined names)
 
-         foreach ($this->definedname as $definedName) {
 
-             if ($definedName['isBuiltInName']) {
 
-                 switch ($definedName['name']) {
 
-                     case pack('C', 0x06):
 
-                         // print area
 
-                         //    in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
 
-                         $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
 
-                         $extractedRanges = [];
 
-                         foreach ($ranges as $range) {
 
-                             // $range should look like one of these
 
-                             //        Foo!$C$7:$J$66
 
-                             //        Bar!$A$1:$IV$2
 
-                             $explodes = Worksheet::extractSheetTitle($range, true);
 
-                             $sheetName = trim($explodes[0], "'");
 
-                             if (count($explodes) == 2) {
 
-                                 if (strpos($explodes[1], ':') === false) {
 
-                                     $explodes[1] = $explodes[1] . ':' . $explodes[1];
 
-                                 }
 
-                                 $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
 
-                             }
 
-                         }
 
-                         if ($docSheet = $this->spreadsheet->getSheetByName($sheetName)) {
 
-                             $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
 
-                         }
 
-                         break;
 
-                     case pack('C', 0x07):
 
-                         // print titles (repeating rows)
 
-                         // Assuming BIFF8, there are 3 cases
 
-                         // 1. repeating rows
 
-                         //        formula looks like this: Sheet!$A$1:$IV$2
 
-                         //        rows 1-2 repeat
 
-                         // 2. repeating columns
 
-                         //        formula looks like this: Sheet!$A$1:$B$65536
 
-                         //        columns A-B repeat
 
-                         // 3. both repeating rows and repeating columns
 
-                         //        formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
 
-                         $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
 
-                         foreach ($ranges as $range) {
 
-                             // $range should look like this one of these
 
-                             //        Sheet!$A$1:$B$65536
 
-                             //        Sheet!$A$1:$IV$2
 
-                             if (strpos($range, '!') !== false) {
 
-                                 $explodes = Worksheet::extractSheetTitle($range, true);
 
-                                 if ($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) {
 
-                                     $extractedRange = $explodes[1];
 
-                                     $extractedRange = str_replace('$', '', $extractedRange);
 
-                                     $coordinateStrings = explode(':', $extractedRange);
 
-                                     if (count($coordinateStrings) == 2) {
 
-                                         list($firstColumn, $firstRow) = Coordinate::coordinateFromString($coordinateStrings[0]);
 
-                                         list($lastColumn, $lastRow) = Coordinate::coordinateFromString($coordinateStrings[1]);
 
-                                         if ($firstColumn == 'A' and $lastColumn == 'IV') {
 
-                                             // then we have repeating rows
 
-                                             $docSheet->getPageSetup()->setRowsToRepeatAtTop([$firstRow, $lastRow]);
 
-                                         } elseif ($firstRow == 1 and $lastRow == 65536) {
 
-                                             // then we have repeating columns
 
-                                             $docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$firstColumn, $lastColumn]);
 
-                                         }
 
-                                     }
 
-                                 }
 
-                             }
 
-                         }
 
-                         break;
 
-                 }
 
-             } else {
 
-                 // Extract range
 
-                 if (strpos($definedName['formula'], '!') !== false) {
 
-                     $explodes = Worksheet::extractSheetTitle($definedName['formula'], true);
 
-                     if (($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) ||
 
-                         ($docSheet = $this->spreadsheet->getSheetByName(trim($explodes[0], "'")))) {
 
-                         $extractedRange = $explodes[1];
 
-                         $extractedRange = str_replace('$', '', $extractedRange);
 
-                         $localOnly = ($definedName['scope'] == 0) ? false : true;
 
-                         $scope = ($definedName['scope'] == 0) ? null : $this->spreadsheet->getSheetByName($this->sheets[$definedName['scope'] - 1]['name']);
 
-                         $this->spreadsheet->addNamedRange(new NamedRange((string) $definedName['name'], $docSheet, $extractedRange, $localOnly, $scope));
 
-                     }
 
-                 }
 
-                 //    Named Value
 
-                     //    TODO Provide support for named values
 
-             }
 
-         }
 
-         $this->data = null;
 
-         return $this->spreadsheet;
 
-     }
 
-     /**
 
-      * Read record data from stream, decrypting as required.
 
-      *
 
-      * @param string $data Data stream to read from
 
-      * @param int $pos Position to start reading from
 
-      * @param int $len Record data length
 
-      *
 
-      * @return string Record data
 
-      */
 
-     private function readRecordData($data, $pos, $len)
 
-     {
 
-         $data = substr($data, $pos, $len);
 
-         // File not encrypted, or record before encryption start point
 
-         if ($this->encryption == self::MS_BIFF_CRYPTO_NONE || $pos < $this->encryptionStartPos) {
 
-             return $data;
 
-         }
 
-         $recordData = '';
 
-         if ($this->encryption == self::MS_BIFF_CRYPTO_RC4) {
 
-             $oldBlock = floor($this->rc4Pos / self::REKEY_BLOCK);
 
-             $block = floor($pos / self::REKEY_BLOCK);
 
-             $endBlock = floor(($pos + $len) / self::REKEY_BLOCK);
 
-             // Spin an RC4 decryptor to the right spot. If we have a decryptor sitting
 
-             // at a point earlier in the current block, re-use it as we can save some time.
 
-             if ($block != $oldBlock || $pos < $this->rc4Pos || !$this->rc4Key) {
 
-                 $this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
 
-                 $step = $pos % self::REKEY_BLOCK;
 
-             } else {
 
-                 $step = $pos - $this->rc4Pos;
 
-             }
 
-             $this->rc4Key->RC4(str_repeat("\0", $step));
 
-             // Decrypt record data (re-keying at the end of every block)
 
-             while ($block != $endBlock) {
 
-                 $step = self::REKEY_BLOCK - ($pos % self::REKEY_BLOCK);
 
-                 $recordData .= $this->rc4Key->RC4(substr($data, 0, $step));
 
-                 $data = substr($data, $step);
 
-                 $pos += $step;
 
-                 $len -= $step;
 
-                 ++$block;
 
-                 $this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
 
-             }
 
-             $recordData .= $this->rc4Key->RC4(substr($data, 0, $len));
 
-             // Keep track of the position of this decryptor.
 
-             // We'll try and re-use it later if we can to speed things up
 
-             $this->rc4Pos = $pos + $len;
 
-         } elseif ($this->encryption == self::MS_BIFF_CRYPTO_XOR) {
 
-             throw new Exception('XOr encryption not supported');
 
-         }
 
-         return $recordData;
 
-     }
 
-     /**
 
-      * Use OLE reader to extract the relevant data streams from the OLE file.
 
-      *
 
-      * @param string $pFilename
 
-      */
 
-     private function loadOLE($pFilename)
 
-     {
 
-         // OLE reader
 
-         $ole = new OLERead();
 
-         // get excel data,
 
-         $ole->read($pFilename);
 
-         // Get workbook data: workbook stream + sheet streams
 
-         $this->data = $ole->getStream($ole->wrkbook);
 
-         // Get summary information data
 
-         $this->summaryInformation = $ole->getStream($ole->summaryInformation);
 
-         // Get additional document summary information data
 
-         $this->documentSummaryInformation = $ole->getStream($ole->documentSummaryInformation);
 
-     }
 
-     /**
 
-      * Read summary information.
 
-      */
 
-     private function readSummaryInformation()
 
-     {
 
-         if (!isset($this->summaryInformation)) {
 
-             return;
 
-         }
 
-         // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
 
-         // offset: 2; size: 2;
 
-         // offset: 4; size: 2; OS version
 
-         // offset: 6; size: 2; OS indicator
 
-         // offset: 8; size: 16
 
-         // offset: 24; size: 4; section count
 
-         $secCount = self::getInt4d($this->summaryInformation, 24);
 
-         // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9
 
-         // offset: 44; size: 4
 
-         $secOffset = self::getInt4d($this->summaryInformation, 44);
 
-         // section header
 
-         // offset: $secOffset; size: 4; section length
 
-         $secLength = self::getInt4d($this->summaryInformation, $secOffset);
 
-         // offset: $secOffset+4; size: 4; property count
 
-         $countProperties = self::getInt4d($this->summaryInformation, $secOffset + 4);
 
-         // initialize code page (used to resolve string values)
 
-         $codePage = 'CP1252';
 
-         // offset: ($secOffset+8); size: var
 
-         // loop through property decarations and properties
 
-         for ($i = 0; $i < $countProperties; ++$i) {
 
-             // offset: ($secOffset+8) + (8 * $i); size: 4; property ID
 
-             $id = self::getInt4d($this->summaryInformation, ($secOffset + 8) + (8 * $i));
 
-             // Use value of property id as appropriate
 
-             // offset: ($secOffset+12) + (8 * $i); size: 4; offset from beginning of section (48)
 
-             $offset = self::getInt4d($this->summaryInformation, ($secOffset + 12) + (8 * $i));
 
-             $type = self::getInt4d($this->summaryInformation, $secOffset + $offset);
 
-             // initialize property value
 
-             $value = null;
 
-             // extract property value based on property type
 
-             switch ($type) {
 
-                 case 0x02: // 2 byte signed integer
 
-                     $value = self::getUInt2d($this->summaryInformation, $secOffset + 4 + $offset);
 
-                     break;
 
-                 case 0x03: // 4 byte signed integer
 
-                     $value = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
 
-                     break;
 
-                 case 0x13: // 4 byte unsigned integer
 
-                     // not needed yet, fix later if necessary
 
-                     break;
 
-                 case 0x1E: // null-terminated string prepended by dword string length
 
-                     $byteLength = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
 
-                     $value = substr($this->summaryInformation, $secOffset + 8 + $offset, $byteLength);
 
-                     $value = StringHelper::convertEncoding($value, 'UTF-8', $codePage);
 
-                     $value = rtrim($value);
 
-                     break;
 
-                 case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
 
-                     // PHP-time
 
-                     $value = OLE::OLE2LocalDate(substr($this->summaryInformation, $secOffset + 4 + $offset, 8));
 
-                     break;
 
-                 case 0x47: // Clipboard format
 
-                     // not needed yet, fix later if necessary
 
-                     break;
 
-             }
 
-             switch ($id) {
 
-                 case 0x01:    //    Code Page
 
-                     $codePage = CodePage::numberToName($value);
 
-                     break;
 
-                 case 0x02:    //    Title
 
-                     $this->spreadsheet->getProperties()->setTitle($value);
 
-                     break;
 
-                 case 0x03:    //    Subject
 
-                     $this->spreadsheet->getProperties()->setSubject($value);
 
-                     break;
 
-                 case 0x04:    //    Author (Creator)
 
-                     $this->spreadsheet->getProperties()->setCreator($value);
 
-                     break;
 
-                 case 0x05:    //    Keywords
 
-                     $this->spreadsheet->getProperties()->setKeywords($value);
 
-                     break;
 
-                 case 0x06:    //    Comments (Description)
 
-                     $this->spreadsheet->getProperties()->setDescription($value);
 
-                     break;
 
-                 case 0x07:    //    Template
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x08:    //    Last Saved By (LastModifiedBy)
 
-                     $this->spreadsheet->getProperties()->setLastModifiedBy($value);
 
-                     break;
 
-                 case 0x09:    //    Revision
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x0A:    //    Total Editing Time
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x0B:    //    Last Printed
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x0C:    //    Created Date/Time
 
-                     $this->spreadsheet->getProperties()->setCreated($value);
 
-                     break;
 
-                 case 0x0D:    //    Modified Date/Time
 
-                     $this->spreadsheet->getProperties()->setModified($value);
 
-                     break;
 
-                 case 0x0E:    //    Number of Pages
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x0F:    //    Number of Words
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x10:    //    Number of Characters
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x11:    //    Thumbnail
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x12:    //    Name of creating application
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x13:    //    Security
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read additional document summary information.
 
-      */
 
-     private function readDocumentSummaryInformation()
 
-     {
 
-         if (!isset($this->documentSummaryInformation)) {
 
-             return;
 
-         }
 
-         //    offset: 0;    size: 2;    must be 0xFE 0xFF (UTF-16 LE byte order mark)
 
-         //    offset: 2;    size: 2;
 
-         //    offset: 4;    size: 2;    OS version
 
-         //    offset: 6;    size: 2;    OS indicator
 
-         //    offset: 8;    size: 16
 
-         //    offset: 24;    size: 4;    section count
 
-         $secCount = self::getInt4d($this->documentSummaryInformation, 24);
 
-         // offset: 28;    size: 16;    first section's class id: 02 d5 cd d5 9c 2e 1b 10 93 97 08 00 2b 2c f9 ae
 
-         // offset: 44;    size: 4;    first section offset
 
-         $secOffset = self::getInt4d($this->documentSummaryInformation, 44);
 
-         //    section header
 
-         //    offset: $secOffset;    size: 4;    section length
 
-         $secLength = self::getInt4d($this->documentSummaryInformation, $secOffset);
 
-         //    offset: $secOffset+4;    size: 4;    property count
 
-         $countProperties = self::getInt4d($this->documentSummaryInformation, $secOffset + 4);
 
-         // initialize code page (used to resolve string values)
 
-         $codePage = 'CP1252';
 
-         //    offset: ($secOffset+8);    size: var
 
-         //    loop through property decarations and properties
 
-         for ($i = 0; $i < $countProperties; ++$i) {
 
-             //    offset: ($secOffset+8) + (8 * $i);    size: 4;    property ID
 
-             $id = self::getInt4d($this->documentSummaryInformation, ($secOffset + 8) + (8 * $i));
 
-             // Use value of property id as appropriate
 
-             // offset: 60 + 8 * $i;    size: 4;    offset from beginning of section (48)
 
-             $offset = self::getInt4d($this->documentSummaryInformation, ($secOffset + 12) + (8 * $i));
 
-             $type = self::getInt4d($this->documentSummaryInformation, $secOffset + $offset);
 
-             // initialize property value
 
-             $value = null;
 
-             // extract property value based on property type
 
-             switch ($type) {
 
-                 case 0x02:    //    2 byte signed integer
 
-                     $value = self::getUInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
 
-                     break;
 
-                 case 0x03:    //    4 byte signed integer
 
-                     $value = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
 
-                     break;
 
-                 case 0x0B:  // Boolean
 
-                     $value = self::getUInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
 
-                     $value = ($value == 0 ? false : true);
 
-                     break;
 
-                 case 0x13:    //    4 byte unsigned integer
 
-                     // not needed yet, fix later if necessary
 
-                     break;
 
-                 case 0x1E:    //    null-terminated string prepended by dword string length
 
-                     $byteLength = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
 
-                     $value = substr($this->documentSummaryInformation, $secOffset + 8 + $offset, $byteLength);
 
-                     $value = StringHelper::convertEncoding($value, 'UTF-8', $codePage);
 
-                     $value = rtrim($value);
 
-                     break;
 
-                 case 0x40:    //    Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
 
-                     // PHP-Time
 
-                     $value = OLE::OLE2LocalDate(substr($this->documentSummaryInformation, $secOffset + 4 + $offset, 8));
 
-                     break;
 
-                 case 0x47:    //    Clipboard format
 
-                     // not needed yet, fix later if necessary
 
-                     break;
 
-             }
 
-             switch ($id) {
 
-                 case 0x01:    //    Code Page
 
-                     $codePage = CodePage::numberToName($value);
 
-                     break;
 
-                 case 0x02:    //    Category
 
-                     $this->spreadsheet->getProperties()->setCategory($value);
 
-                     break;
 
-                 case 0x03:    //    Presentation Target
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x04:    //    Bytes
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x05:    //    Lines
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x06:    //    Paragraphs
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x07:    //    Slides
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x08:    //    Notes
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x09:    //    Hidden Slides
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x0A:    //    MM Clips
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x0B:    //    Scale Crop
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x0C:    //    Heading Pairs
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x0D:    //    Titles of Parts
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-                 case 0x0E:    //    Manager
 
-                     $this->spreadsheet->getProperties()->setManager($value);
 
-                     break;
 
-                 case 0x0F:    //    Company
 
-                     $this->spreadsheet->getProperties()->setCompany($value);
 
-                     break;
 
-                 case 0x10:    //    Links up-to-date
 
-                     //    Not supported by PhpSpreadsheet
 
-                     break;
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
 
-      */
 
-     private function readDefault()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-     }
 
-     /**
 
-      *    The NOTE record specifies a comment associated with a particular cell. In Excel 95 (BIFF7) and earlier versions,
 
-      *        this record stores a note (cell note). This feature was significantly enhanced in Excel 97.
 
-      */
 
-     private function readNote()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if ($this->readDataOnly) {
 
-             return;
 
-         }
 
-         $cellAddress = $this->readBIFF8CellAddress(substr($recordData, 0, 4));
 
-         if ($this->version == self::XLS_BIFF8) {
 
-             $noteObjID = self::getUInt2d($recordData, 6);
 
-             $noteAuthor = self::readUnicodeStringLong(substr($recordData, 8));
 
-             $noteAuthor = $noteAuthor['value'];
 
-             $this->cellNotes[$noteObjID] = [
 
-                 'cellRef' => $cellAddress,
 
-                 'objectID' => $noteObjID,
 
-                 'author' => $noteAuthor,
 
-             ];
 
-         } else {
 
-             $extension = false;
 
-             if ($cellAddress == '$B$65536') {
 
-                 //    If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation
 
-                 //        note from the previous cell annotation. We're not yet handling this, so annotations longer than the
 
-                 //        max 2048 bytes will probably throw a wobbly.
 
-                 $row = self::getUInt2d($recordData, 0);
 
-                 $extension = true;
 
-                 $cellAddress = array_pop(array_keys($this->phpSheet->getComments()));
 
-             }
 
-             $cellAddress = str_replace('$', '', $cellAddress);
 
-             $noteLength = self::getUInt2d($recordData, 4);
 
-             $noteText = trim(substr($recordData, 6));
 
-             if ($extension) {
 
-                 //    Concatenate this extension with the currently set comment for the cell
 
-                 $comment = $this->phpSheet->getComment($cellAddress);
 
-                 $commentText = $comment->getText()->getPlainText();
 
-                 $comment->setText($this->parseRichText($commentText . $noteText));
 
-             } else {
 
-                 //    Set comment for the cell
 
-                 $this->phpSheet->getComment($cellAddress)->setText($this->parseRichText($noteText));
 
- //                                                    ->setAuthor($author)
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * The TEXT Object record contains the text associated with a cell annotation.
 
-      */
 
-     private function readTextObject()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if ($this->readDataOnly) {
 
-             return;
 
-         }
 
-         // recordData consists of an array of subrecords looking like this:
 
-         //    grbit: 2 bytes; Option Flags
 
-         //    rot: 2 bytes; rotation
 
-         //    cchText: 2 bytes; length of the text (in the first continue record)
 
-         //    cbRuns: 2 bytes; length of the formatting (in the second continue record)
 
-         // followed by the continuation records containing the actual text and formatting
 
-         $grbitOpts = self::getUInt2d($recordData, 0);
 
-         $rot = self::getUInt2d($recordData, 2);
 
-         $cchText = self::getUInt2d($recordData, 10);
 
-         $cbRuns = self::getUInt2d($recordData, 12);
 
-         $text = $this->getSplicedRecordData();
 
-         $textByte = $text['spliceOffsets'][1] - $text['spliceOffsets'][0] - 1;
 
-         $textStr = substr($text['recordData'], $text['spliceOffsets'][0] + 1, $textByte);
 
-         // get 1 byte
 
-         $is16Bit = ord($text['recordData'][0]);
 
-         // it is possible to use a compressed format,
 
-         // which omits the high bytes of all characters, if they are all zero
 
-         if (($is16Bit & 0x01) === 0) {
 
-             $textStr = StringHelper::ConvertEncoding($textStr, 'UTF-8', 'ISO-8859-1');
 
-         } else {
 
-             $textStr = $this->decodeCodepage($textStr);
 
-         }
 
-         $this->textObjects[$this->textObjRef] = [
 
-             'text' => $textStr,
 
-             'format' => substr($text['recordData'], $text['spliceOffsets'][1], $cbRuns),
 
-             'alignment' => $grbitOpts,
 
-             'rotation' => $rot,
 
-         ];
 
-     }
 
-     /**
 
-      * Read BOF.
 
-      */
 
-     private function readBof()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = substr($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 2; size: 2; type of the following data
 
-         $substreamType = self::getUInt2d($recordData, 2);
 
-         switch ($substreamType) {
 
-             case self::XLS_WORKBOOKGLOBALS:
 
-                 $version = self::getUInt2d($recordData, 0);
 
-                 if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
 
-                     throw new Exception('Cannot read this Excel file. Version is too old.');
 
-                 }
 
-                 $this->version = $version;
 
-                 break;
 
-             case self::XLS_WORKSHEET:
 
-                 // do not use this version information for anything
 
-                 // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
 
-                 break;
 
-             default:
 
-                 // substream, e.g. chart
 
-                 // just skip the entire substream
 
-                 do {
 
-                     $code = self::getUInt2d($this->data, $this->pos);
 
-                     $this->readDefault();
 
-                 } while ($code != self::XLS_TYPE_EOF && $this->pos < $this->dataSize);
 
-                 break;
 
-         }
 
-     }
 
-     /**
 
-      * FILEPASS.
 
-      *
 
-      * This record is part of the File Protection Block. It
 
-      * contains information about the read/write password of the
 
-      * file. All record contents following this record will be
 
-      * encrypted.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      *
 
-      * The decryption functions and objects used from here on in
 
-      * are based on the source of Spreadsheet-ParseExcel:
 
-      * https://metacpan.org/release/Spreadsheet-ParseExcel
 
-      */
 
-     private function readFilepass()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         if ($length != 54) {
 
-             throw new Exception('Unexpected file pass record length');
 
-         }
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->verifyPassword('VelvetSweatshop', substr($recordData, 6, 16), substr($recordData, 22, 16), substr($recordData, 38, 16), $this->md5Ctxt)) {
 
-             throw new Exception('Decryption password incorrect');
 
-         }
 
-         $this->encryption = self::MS_BIFF_CRYPTO_RC4;
 
-         // Decryption required from the record after next onwards
 
-         $this->encryptionStartPos = $this->pos + self::getUInt2d($this->data, $this->pos + 2);
 
-     }
 
-     /**
 
-      * Make an RC4 decryptor for the given block.
 
-      *
 
-      * @param int $block Block for which to create decrypto
 
-      * @param string $valContext MD5 context state
 
-      *
 
-      * @return Xls\RC4
 
-      */
 
-     private function makeKey($block, $valContext)
 
-     {
 
-         $pwarray = str_repeat("\0", 64);
 
-         for ($i = 0; $i < 5; ++$i) {
 
-             $pwarray[$i] = $valContext[$i];
 
-         }
 
-         $pwarray[5] = chr($block & 0xff);
 
-         $pwarray[6] = chr(($block >> 8) & 0xff);
 
-         $pwarray[7] = chr(($block >> 16) & 0xff);
 
-         $pwarray[8] = chr(($block >> 24) & 0xff);
 
-         $pwarray[9] = "\x80";
 
-         $pwarray[56] = "\x48";
 
-         $md5 = new Xls\MD5();
 
-         $md5->add($pwarray);
 
-         $s = $md5->getContext();
 
-         return new Xls\RC4($s);
 
-     }
 
-     /**
 
-      * Verify RC4 file password.
 
-      *
 
-      * @param string $password Password to check
 
-      * @param string $docid Document id
 
-      * @param string $salt_data Salt data
 
-      * @param string $hashedsalt_data Hashed salt data
 
-      * @param string $valContext Set to the MD5 context of the value
 
-      *
 
-      * @return bool Success
 
-      */
 
-     private function verifyPassword($password, $docid, $salt_data, $hashedsalt_data, &$valContext)
 
-     {
 
-         $pwarray = str_repeat("\0", 64);
 
-         $iMax = strlen($password);
 
-         for ($i = 0; $i < $iMax; ++$i) {
 
-             $o = ord(substr($password, $i, 1));
 
-             $pwarray[2 * $i] = chr($o & 0xff);
 
-             $pwarray[2 * $i + 1] = chr(($o >> 8) & 0xff);
 
-         }
 
-         $pwarray[2 * $i] = chr(0x80);
 
-         $pwarray[56] = chr(($i << 4) & 0xff);
 
-         $md5 = new Xls\MD5();
 
-         $md5->add($pwarray);
 
-         $mdContext1 = $md5->getContext();
 
-         $offset = 0;
 
-         $keyoffset = 0;
 
-         $tocopy = 5;
 
-         $md5->reset();
 
-         while ($offset != 16) {
 
-             if ((64 - $offset) < 5) {
 
-                 $tocopy = 64 - $offset;
 
-             }
 
-             for ($i = 0; $i <= $tocopy; ++$i) {
 
-                 $pwarray[$offset + $i] = $mdContext1[$keyoffset + $i];
 
-             }
 
-             $offset += $tocopy;
 
-             if ($offset == 64) {
 
-                 $md5->add($pwarray);
 
-                 $keyoffset = $tocopy;
 
-                 $tocopy = 5 - $tocopy;
 
-                 $offset = 0;
 
-                 continue;
 
-             }
 
-             $keyoffset = 0;
 
-             $tocopy = 5;
 
-             for ($i = 0; $i < 16; ++$i) {
 
-                 $pwarray[$offset + $i] = $docid[$i];
 
-             }
 
-             $offset += 16;
 
-         }
 
-         $pwarray[16] = "\x80";
 
-         for ($i = 0; $i < 47; ++$i) {
 
-             $pwarray[17 + $i] = "\0";
 
-         }
 
-         $pwarray[56] = "\x80";
 
-         $pwarray[57] = "\x0a";
 
-         $md5->add($pwarray);
 
-         $valContext = $md5->getContext();
 
-         $key = $this->makeKey(0, $valContext);
 
-         $salt = $key->RC4($salt_data);
 
-         $hashedsalt = $key->RC4($hashedsalt_data);
 
-         $salt .= "\x80" . str_repeat("\0", 47);
 
-         $salt[56] = "\x80";
 
-         $md5->reset();
 
-         $md5->add($salt);
 
-         $mdContext2 = $md5->getContext();
 
-         return $mdContext2 == $hashedsalt;
 
-     }
 
-     /**
 
-      * CODEPAGE.
 
-      *
 
-      * This record stores the text encoding used to write byte
 
-      * strings, stored as MS Windows code page identifier.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readCodepage()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2; code page identifier
 
-         $codepage = self::getUInt2d($recordData, 0);
 
-         $this->codepage = CodePage::numberToName($codepage);
 
-     }
 
-     /**
 
-      * DATEMODE.
 
-      *
 
-      * This record specifies the base date for displaying date
 
-      * values. All dates are stored as count of days past this
 
-      * base date. In BIFF2-BIFF4 this record is part of the
 
-      * Calculation Settings Block. In BIFF5-BIFF8 it is
 
-      * stored in the Workbook Globals Substream.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readDateMode()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2; 0 = base 1900, 1 = base 1904
 
-         Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
 
-         if (ord($recordData[0]) == 1) {
 
-             Date::setExcelCalendar(Date::CALENDAR_MAC_1904);
 
-         }
 
-     }
 
-     /**
 
-      * Read a FONT record.
 
-      */
 
-     private function readFont()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             $objFont = new Font();
 
-             // offset: 0; size: 2; height of the font (in twips = 1/20 of a point)
 
-             $size = self::getUInt2d($recordData, 0);
 
-             $objFont->setSize($size / 20);
 
-             // offset: 2; size: 2; option flags
 
-             // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8)
 
-             // bit: 1; mask 0x0002; italic
 
-             $isItalic = (0x0002 & self::getUInt2d($recordData, 2)) >> 1;
 
-             if ($isItalic) {
 
-                 $objFont->setItalic(true);
 
-             }
 
-             // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8)
 
-             // bit: 3; mask 0x0008; strikethrough
 
-             $isStrike = (0x0008 & self::getUInt2d($recordData, 2)) >> 3;
 
-             if ($isStrike) {
 
-                 $objFont->setStrikethrough(true);
 
-             }
 
-             // offset: 4; size: 2; colour index
 
-             $colorIndex = self::getUInt2d($recordData, 4);
 
-             $objFont->colorIndex = $colorIndex;
 
-             // offset: 6; size: 2; font weight
 
-             $weight = self::getUInt2d($recordData, 6);
 
-             switch ($weight) {
 
-                 case 0x02BC:
 
-                     $objFont->setBold(true);
 
-                     break;
 
-             }
 
-             // offset: 8; size: 2; escapement type
 
-             $escapement = self::getUInt2d($recordData, 8);
 
-             switch ($escapement) {
 
-                 case 0x0001:
 
-                     $objFont->setSuperscript(true);
 
-                     break;
 
-                 case 0x0002:
 
-                     $objFont->setSubscript(true);
 
-                     break;
 
-             }
 
-             // offset: 10; size: 1; underline type
 
-             $underlineType = ord($recordData[10]);
 
-             switch ($underlineType) {
 
-                 case 0x00:
 
-                     break; // no underline
 
-                 case 0x01:
 
-                     $objFont->setUnderline(Font::UNDERLINE_SINGLE);
 
-                     break;
 
-                 case 0x02:
 
-                     $objFont->setUnderline(Font::UNDERLINE_DOUBLE);
 
-                     break;
 
-                 case 0x21:
 
-                     $objFont->setUnderline(Font::UNDERLINE_SINGLEACCOUNTING);
 
-                     break;
 
-                 case 0x22:
 
-                     $objFont->setUnderline(Font::UNDERLINE_DOUBLEACCOUNTING);
 
-                     break;
 
-             }
 
-             // offset: 11; size: 1; font family
 
-             // offset: 12; size: 1; character set
 
-             // offset: 13; size: 1; not used
 
-             // offset: 14; size: var; font name
 
-             if ($this->version == self::XLS_BIFF8) {
 
-                 $string = self::readUnicodeStringShort(substr($recordData, 14));
 
-             } else {
 
-                 $string = $this->readByteStringShort(substr($recordData, 14));
 
-             }
 
-             $objFont->setName($string['value']);
 
-             $this->objFonts[] = $objFont;
 
-         }
 
-     }
 
-     /**
 
-      * FORMAT.
 
-      *
 
-      * This record contains information about a number format.
 
-      * All FORMAT records occur together in a sequential list.
 
-      *
 
-      * In BIFF2-BIFF4 other records referencing a FORMAT record
 
-      * contain a zero-based index into this list. From BIFF5 on
 
-      * the FORMAT record contains the index itself that will be
 
-      * used by other records.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readFormat()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             $indexCode = self::getUInt2d($recordData, 0);
 
-             if ($this->version == self::XLS_BIFF8) {
 
-                 $string = self::readUnicodeStringLong(substr($recordData, 2));
 
-             } else {
 
-                 // BIFF7
 
-                 $string = $this->readByteStringShort(substr($recordData, 2));
 
-             }
 
-             $formatString = $string['value'];
 
-             $this->formats[$indexCode] = $formatString;
 
-         }
 
-     }
 
-     /**
 
-      * XF - Extended Format.
 
-      *
 
-      * This record contains formatting information for cells, rows, columns or styles.
 
-      * According to https://support.microsoft.com/en-us/help/147732 there are always at least 15 cell style XF
 
-      * and 1 cell XF.
 
-      * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF
 
-      * and XF record 15 is a cell XF
 
-      * We only read the first cell style XF and skip the remaining cell style XF records
 
-      * We read all cell XF records.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readXf()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         $objStyle = new Style();
 
-         if (!$this->readDataOnly) {
 
-             // offset:  0; size: 2; Index to FONT record
 
-             if (self::getUInt2d($recordData, 0) < 4) {
 
-                 $fontIndex = self::getUInt2d($recordData, 0);
 
-             } else {
 
-                 // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
 
-                 // check the OpenOffice documentation of the FONT record
 
-                 $fontIndex = self::getUInt2d($recordData, 0) - 1;
 
-             }
 
-             $objStyle->setFont($this->objFonts[$fontIndex]);
 
-             // offset:  2; size: 2; Index to FORMAT record
 
-             $numberFormatIndex = self::getUInt2d($recordData, 2);
 
-             if (isset($this->formats[$numberFormatIndex])) {
 
-                 // then we have user-defined format code
 
-                 $numberFormat = ['formatCode' => $this->formats[$numberFormatIndex]];
 
-             } elseif (($code = NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') {
 
-                 // then we have built-in format code
 
-                 $numberFormat = ['formatCode' => $code];
 
-             } else {
 
-                 // we set the general format code
 
-                 $numberFormat = ['formatCode' => 'General'];
 
-             }
 
-             $objStyle->getNumberFormat()->setFormatCode($numberFormat['formatCode']);
 
-             // offset:  4; size: 2; XF type, cell protection, and parent style XF
 
-             // bit 2-0; mask 0x0007; XF_TYPE_PROT
 
-             $xfTypeProt = self::getUInt2d($recordData, 4);
 
-             // bit 0; mask 0x01; 1 = cell is locked
 
-             $isLocked = (0x01 & $xfTypeProt) >> 0;
 
-             $objStyle->getProtection()->setLocked($isLocked ? Protection::PROTECTION_INHERIT : Protection::PROTECTION_UNPROTECTED);
 
-             // bit 1; mask 0x02; 1 = Formula is hidden
 
-             $isHidden = (0x02 & $xfTypeProt) >> 1;
 
-             $objStyle->getProtection()->setHidden($isHidden ? Protection::PROTECTION_PROTECTED : Protection::PROTECTION_UNPROTECTED);
 
-             // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF
 
-             $isCellStyleXf = (0x04 & $xfTypeProt) >> 2;
 
-             // offset:  6; size: 1; Alignment and text break
 
-             // bit 2-0, mask 0x07; horizontal alignment
 
-             $horAlign = (0x07 & ord($recordData[6])) >> 0;
 
-             switch ($horAlign) {
 
-                 case 0:
 
-                     $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_GENERAL);
 
-                     break;
 
-                 case 1:
 
-                     $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
 
-                     break;
 
-                 case 2:
 
-                     $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
 
-                     break;
 
-                 case 3:
 
-                     $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
 
-                     break;
 
-                 case 4:
 
-                     $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_FILL);
 
-                     break;
 
-                 case 5:
 
-                     $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_JUSTIFY);
 
-                     break;
 
-                 case 6:
 
-                     $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER_CONTINUOUS);
 
-                     break;
 
-             }
 
-             // bit 3, mask 0x08; wrap text
 
-             $wrapText = (0x08 & ord($recordData[6])) >> 3;
 
-             switch ($wrapText) {
 
-                 case 0:
 
-                     $objStyle->getAlignment()->setWrapText(false);
 
-                     break;
 
-                 case 1:
 
-                     $objStyle->getAlignment()->setWrapText(true);
 
-                     break;
 
-             }
 
-             // bit 6-4, mask 0x70; vertical alignment
 
-             $vertAlign = (0x70 & ord($recordData[6])) >> 4;
 
-             switch ($vertAlign) {
 
-                 case 0:
 
-                     $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_TOP);
 
-                     break;
 
-                 case 1:
 
-                     $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
 
-                     break;
 
-                 case 2:
 
-                     $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_BOTTOM);
 
-                     break;
 
-                 case 3:
 
-                     $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_JUSTIFY);
 
-                     break;
 
-             }
 
-             if ($this->version == self::XLS_BIFF8) {
 
-                 // offset:  7; size: 1; XF_ROTATION: Text rotation angle
 
-                 $angle = ord($recordData[7]);
 
-                 $rotation = 0;
 
-                 if ($angle <= 90) {
 
-                     $rotation = $angle;
 
-                 } elseif ($angle <= 180) {
 
-                     $rotation = 90 - $angle;
 
-                 } elseif ($angle == 255) {
 
-                     $rotation = -165;
 
-                 }
 
-                 $objStyle->getAlignment()->setTextRotation($rotation);
 
-                 // offset:  8; size: 1; Indentation, shrink to cell size, and text direction
 
-                 // bit: 3-0; mask: 0x0F; indent level
 
-                 $indent = (0x0F & ord($recordData[8])) >> 0;
 
-                 $objStyle->getAlignment()->setIndent($indent);
 
-                 // bit: 4; mask: 0x10; 1 = shrink content to fit into cell
 
-                 $shrinkToFit = (0x10 & ord($recordData[8])) >> 4;
 
-                 switch ($shrinkToFit) {
 
-                     case 0:
 
-                         $objStyle->getAlignment()->setShrinkToFit(false);
 
-                         break;
 
-                     case 1:
 
-                         $objStyle->getAlignment()->setShrinkToFit(true);
 
-                         break;
 
-                 }
 
-                 // offset:  9; size: 1; Flags used for attribute groups
 
-                 // offset: 10; size: 4; Cell border lines and background area
 
-                 // bit: 3-0; mask: 0x0000000F; left style
 
-                 if ($bordersLeftStyle = Xls\Style\Border::lookup((0x0000000F & self::getInt4d($recordData, 10)) >> 0)) {
 
-                     $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle);
 
-                 }
 
-                 // bit: 7-4; mask: 0x000000F0; right style
 
-                 if ($bordersRightStyle = Xls\Style\Border::lookup((0x000000F0 & self::getInt4d($recordData, 10)) >> 4)) {
 
-                     $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle);
 
-                 }
 
-                 // bit: 11-8; mask: 0x00000F00; top style
 
-                 if ($bordersTopStyle = Xls\Style\Border::lookup((0x00000F00 & self::getInt4d($recordData, 10)) >> 8)) {
 
-                     $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle);
 
-                 }
 
-                 // bit: 15-12; mask: 0x0000F000; bottom style
 
-                 if ($bordersBottomStyle = Xls\Style\Border::lookup((0x0000F000 & self::getInt4d($recordData, 10)) >> 12)) {
 
-                     $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle);
 
-                 }
 
-                 // bit: 22-16; mask: 0x007F0000; left color
 
-                 $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & self::getInt4d($recordData, 10)) >> 16;
 
-                 // bit: 29-23; mask: 0x3F800000; right color
 
-                 $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & self::getInt4d($recordData, 10)) >> 23;
 
-                 // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
 
-                 $diagonalDown = (0x40000000 & self::getInt4d($recordData, 10)) >> 30 ? true : false;
 
-                 // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
 
-                 $diagonalUp = (0x80000000 & self::getInt4d($recordData, 10)) >> 31 ? true : false;
 
-                 if ($diagonalUp == false && $diagonalDown == false) {
 
-                     $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_NONE);
 
-                 } elseif ($diagonalUp == true && $diagonalDown == false) {
 
-                     $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_UP);
 
-                 } elseif ($diagonalUp == false && $diagonalDown == true) {
 
-                     $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_DOWN);
 
-                 } elseif ($diagonalUp == true && $diagonalDown == true) {
 
-                     $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_BOTH);
 
-                 }
 
-                 // offset: 14; size: 4;
 
-                 // bit: 6-0; mask: 0x0000007F; top color
 
-                 $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & self::getInt4d($recordData, 14)) >> 0;
 
-                 // bit: 13-7; mask: 0x00003F80; bottom color
 
-                 $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & self::getInt4d($recordData, 14)) >> 7;
 
-                 // bit: 20-14; mask: 0x001FC000; diagonal color
 
-                 $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & self::getInt4d($recordData, 14)) >> 14;
 
-                 // bit: 24-21; mask: 0x01E00000; diagonal style
 
-                 if ($bordersDiagonalStyle = Xls\Style\Border::lookup((0x01E00000 & self::getInt4d($recordData, 14)) >> 21)) {
 
-                     $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle);
 
-                 }
 
-                 // bit: 31-26; mask: 0xFC000000 fill pattern
 
-                 if ($fillType = Xls\Style\FillPattern::lookup((0xFC000000 & self::getInt4d($recordData, 14)) >> 26)) {
 
-                     $objStyle->getFill()->setFillType($fillType);
 
-                 }
 
-                 // offset: 18; size: 2; pattern and background colour
 
-                 // bit: 6-0; mask: 0x007F; color index for pattern color
 
-                 $objStyle->getFill()->startcolorIndex = (0x007F & self::getUInt2d($recordData, 18)) >> 0;
 
-                 // bit: 13-7; mask: 0x3F80; color index for pattern background
 
-                 $objStyle->getFill()->endcolorIndex = (0x3F80 & self::getUInt2d($recordData, 18)) >> 7;
 
-             } else {
 
-                 // BIFF5
 
-                 // offset: 7; size: 1; Text orientation and flags
 
-                 $orientationAndFlags = ord($recordData[7]);
 
-                 // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation
 
-                 $xfOrientation = (0x03 & $orientationAndFlags) >> 0;
 
-                 switch ($xfOrientation) {
 
-                     case 0:
 
-                         $objStyle->getAlignment()->setTextRotation(0);
 
-                         break;
 
-                     case 1:
 
-                         $objStyle->getAlignment()->setTextRotation(-165);
 
-                         break;
 
-                     case 2:
 
-                         $objStyle->getAlignment()->setTextRotation(90);
 
-                         break;
 
-                     case 3:
 
-                         $objStyle->getAlignment()->setTextRotation(-90);
 
-                         break;
 
-                 }
 
-                 // offset: 8; size: 4; cell border lines and background area
 
-                 $borderAndBackground = self::getInt4d($recordData, 8);
 
-                 // bit: 6-0; mask: 0x0000007F; color index for pattern color
 
-                 $objStyle->getFill()->startcolorIndex = (0x0000007F & $borderAndBackground) >> 0;
 
-                 // bit: 13-7; mask: 0x00003F80; color index for pattern background
 
-                 $objStyle->getFill()->endcolorIndex = (0x00003F80 & $borderAndBackground) >> 7;
 
-                 // bit: 21-16; mask: 0x003F0000; fill pattern
 
-                 $objStyle->getFill()->setFillType(Xls\Style\FillPattern::lookup((0x003F0000 & $borderAndBackground) >> 16));
 
-                 // bit: 24-22; mask: 0x01C00000; bottom line style
 
-                 $objStyle->getBorders()->getBottom()->setBorderStyle(Xls\Style\Border::lookup((0x01C00000 & $borderAndBackground) >> 22));
 
-                 // bit: 31-25; mask: 0xFE000000; bottom line color
 
-                 $objStyle->getBorders()->getBottom()->colorIndex = (0xFE000000 & $borderAndBackground) >> 25;
 
-                 // offset: 12; size: 4; cell border lines
 
-                 $borderLines = self::getInt4d($recordData, 12);
 
-                 // bit: 2-0; mask: 0x00000007; top line style
 
-                 $objStyle->getBorders()->getTop()->setBorderStyle(Xls\Style\Border::lookup((0x00000007 & $borderLines) >> 0));
 
-                 // bit: 5-3; mask: 0x00000038; left line style
 
-                 $objStyle->getBorders()->getLeft()->setBorderStyle(Xls\Style\Border::lookup((0x00000038 & $borderLines) >> 3));
 
-                 // bit: 8-6; mask: 0x000001C0; right line style
 
-                 $objStyle->getBorders()->getRight()->setBorderStyle(Xls\Style\Border::lookup((0x000001C0 & $borderLines) >> 6));
 
-                 // bit: 15-9; mask: 0x0000FE00; top line color index
 
-                 $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9;
 
-                 // bit: 22-16; mask: 0x007F0000; left line color index
 
-                 $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16;
 
-                 // bit: 29-23; mask: 0x3F800000; right line color index
 
-                 $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23;
 
-             }
 
-             // add cellStyleXf or cellXf and update mapping
 
-             if ($isCellStyleXf) {
 
-                 // we only read one style XF record which is always the first
 
-                 if ($this->xfIndex == 0) {
 
-                     $this->spreadsheet->addCellStyleXf($objStyle);
 
-                     $this->mapCellStyleXfIndex[$this->xfIndex] = 0;
 
-                 }
 
-             } else {
 
-                 // we read all cell XF records
 
-                 $this->spreadsheet->addCellXf($objStyle);
 
-                 $this->mapCellXfIndex[$this->xfIndex] = count($this->spreadsheet->getCellXfCollection()) - 1;
 
-             }
 
-             // update XF index for when we read next record
 
-             ++$this->xfIndex;
 
-         }
 
-     }
 
-     private function readXfExt()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 2; 0x087D = repeated header
 
-             // offset: 2; size: 2
 
-             // offset: 4; size: 8; not used
 
-             // offset: 12; size: 2; record version
 
-             // offset: 14; size: 2; index to XF record which this record modifies
 
-             $ixfe = self::getUInt2d($recordData, 14);
 
-             // offset: 16; size: 2; not used
 
-             // offset: 18; size: 2; number of extension properties that follow
 
-             $cexts = self::getUInt2d($recordData, 18);
 
-             // start reading the actual extension data
 
-             $offset = 20;
 
-             while ($offset < $length) {
 
-                 // extension type
 
-                 $extType = self::getUInt2d($recordData, $offset);
 
-                 // extension length
 
-                 $cb = self::getUInt2d($recordData, $offset + 2);
 
-                 // extension data
 
-                 $extData = substr($recordData, $offset + 4, $cb);
 
-                 switch ($extType) {
 
-                     case 4:        // fill start color
 
-                         $xclfType = self::getUInt2d($extData, 0); // color type
 
-                         $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
 
-                         if ($xclfType == 2) {
 
-                             $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
 
-                             // modify the relevant style property
 
-                             if (isset($this->mapCellXfIndex[$ixfe])) {
 
-                                 $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
 
-                                 $fill->getStartColor()->setRGB($rgb);
 
-                                 unset($fill->startcolorIndex); // normal color index does not apply, discard
 
-                             }
 
-                         }
 
-                         break;
 
-                     case 5:        // fill end color
 
-                         $xclfType = self::getUInt2d($extData, 0); // color type
 
-                         $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
 
-                         if ($xclfType == 2) {
 
-                             $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
 
-                             // modify the relevant style property
 
-                             if (isset($this->mapCellXfIndex[$ixfe])) {
 
-                                 $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
 
-                                 $fill->getEndColor()->setRGB($rgb);
 
-                                 unset($fill->endcolorIndex); // normal color index does not apply, discard
 
-                             }
 
-                         }
 
-                         break;
 
-                     case 7:        // border color top
 
-                         $xclfType = self::getUInt2d($extData, 0); // color type
 
-                         $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
 
-                         if ($xclfType == 2) {
 
-                             $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
 
-                             // modify the relevant style property
 
-                             if (isset($this->mapCellXfIndex[$ixfe])) {
 
-                                 $top = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getTop();
 
-                                 $top->getColor()->setRGB($rgb);
 
-                                 unset($top->colorIndex); // normal color index does not apply, discard
 
-                             }
 
-                         }
 
-                         break;
 
-                     case 8:        // border color bottom
 
-                         $xclfType = self::getUInt2d($extData, 0); // color type
 
-                         $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
 
-                         if ($xclfType == 2) {
 
-                             $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
 
-                             // modify the relevant style property
 
-                             if (isset($this->mapCellXfIndex[$ixfe])) {
 
-                                 $bottom = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getBottom();
 
-                                 $bottom->getColor()->setRGB($rgb);
 
-                                 unset($bottom->colorIndex); // normal color index does not apply, discard
 
-                             }
 
-                         }
 
-                         break;
 
-                     case 9:        // border color left
 
-                         $xclfType = self::getUInt2d($extData, 0); // color type
 
-                         $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
 
-                         if ($xclfType == 2) {
 
-                             $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
 
-                             // modify the relevant style property
 
-                             if (isset($this->mapCellXfIndex[$ixfe])) {
 
-                                 $left = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getLeft();
 
-                                 $left->getColor()->setRGB($rgb);
 
-                                 unset($left->colorIndex); // normal color index does not apply, discard
 
-                             }
 
-                         }
 
-                         break;
 
-                     case 10:        // border color right
 
-                         $xclfType = self::getUInt2d($extData, 0); // color type
 
-                         $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
 
-                         if ($xclfType == 2) {
 
-                             $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
 
-                             // modify the relevant style property
 
-                             if (isset($this->mapCellXfIndex[$ixfe])) {
 
-                                 $right = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getRight();
 
-                                 $right->getColor()->setRGB($rgb);
 
-                                 unset($right->colorIndex); // normal color index does not apply, discard
 
-                             }
 
-                         }
 
-                         break;
 
-                     case 11:        // border color diagonal
 
-                         $xclfType = self::getUInt2d($extData, 0); // color type
 
-                         $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
 
-                         if ($xclfType == 2) {
 
-                             $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
 
-                             // modify the relevant style property
 
-                             if (isset($this->mapCellXfIndex[$ixfe])) {
 
-                                 $diagonal = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getDiagonal();
 
-                                 $diagonal->getColor()->setRGB($rgb);
 
-                                 unset($diagonal->colorIndex); // normal color index does not apply, discard
 
-                             }
 
-                         }
 
-                         break;
 
-                     case 13:    // font color
 
-                         $xclfType = self::getUInt2d($extData, 0); // color type
 
-                         $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
 
-                         if ($xclfType == 2) {
 
-                             $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
 
-                             // modify the relevant style property
 
-                             if (isset($this->mapCellXfIndex[$ixfe])) {
 
-                                 $font = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFont();
 
-                                 $font->getColor()->setRGB($rgb);
 
-                                 unset($font->colorIndex); // normal color index does not apply, discard
 
-                             }
 
-                         }
 
-                         break;
 
-                 }
 
-                 $offset += $cb;
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read STYLE record.
 
-      */
 
-     private function readStyle()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 2; index to XF record and flag for built-in style
 
-             $ixfe = self::getUInt2d($recordData, 0);
 
-             // bit: 11-0; mask 0x0FFF; index to XF record
 
-             $xfIndex = (0x0FFF & $ixfe) >> 0;
 
-             // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style
 
-             $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15);
 
-             if ($isBuiltIn) {
 
-                 // offset: 2; size: 1; identifier for built-in style
 
-                 $builtInId = ord($recordData[2]);
 
-                 switch ($builtInId) {
 
-                     case 0x00:
 
-                         // currently, we are not using this for anything
 
-                         break;
 
-                     default:
 
-                         break;
 
-                 }
 
-             }
 
-             // user-defined; not supported by PhpSpreadsheet
 
-         }
 
-     }
 
-     /**
 
-      * Read PALETTE record.
 
-      */
 
-     private function readPalette()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 2; number of following colors
 
-             $nm = self::getUInt2d($recordData, 0);
 
-             // list of RGB colors
 
-             for ($i = 0; $i < $nm; ++$i) {
 
-                 $rgb = substr($recordData, 2 + 4 * $i, 4);
 
-                 $this->palette[] = self::readRGB($rgb);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * SHEET.
 
-      *
 
-      * This record is  located in the  Workbook Globals
 
-      * Substream  and represents a sheet inside the workbook.
 
-      * One SHEET record is written for each sheet. It stores the
 
-      * sheet name and a stream offset to the BOF record of the
 
-      * respective Sheet Substream within the Workbook Stream.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readSheet()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // offset: 0; size: 4; absolute stream position of the BOF record of the sheet
 
-         // NOTE: not encrypted
 
-         $rec_offset = self::getInt4d($this->data, $this->pos + 4);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 4; size: 1; sheet state
 
-         switch (ord($recordData[4])) {
 
-             case 0x00:
 
-                 $sheetState = Worksheet::SHEETSTATE_VISIBLE;
 
-                 break;
 
-             case 0x01:
 
-                 $sheetState = Worksheet::SHEETSTATE_HIDDEN;
 
-                 break;
 
-             case 0x02:
 
-                 $sheetState = Worksheet::SHEETSTATE_VERYHIDDEN;
 
-                 break;
 
-             default:
 
-                 $sheetState = Worksheet::SHEETSTATE_VISIBLE;
 
-                 break;
 
-         }
 
-         // offset: 5; size: 1; sheet type
 
-         $sheetType = ord($recordData[5]);
 
-         // offset: 6; size: var; sheet name
 
-         if ($this->version == self::XLS_BIFF8) {
 
-             $string = self::readUnicodeStringShort(substr($recordData, 6));
 
-             $rec_name = $string['value'];
 
-         } elseif ($this->version == self::XLS_BIFF7) {
 
-             $string = $this->readByteStringShort(substr($recordData, 6));
 
-             $rec_name = $string['value'];
 
-         }
 
-         $this->sheets[] = [
 
-             'name' => $rec_name,
 
-             'offset' => $rec_offset,
 
-             'sheetState' => $sheetState,
 
-             'sheetType' => $sheetType,
 
-         ];
 
-     }
 
-     /**
 
-      * Read EXTERNALBOOK record.
 
-      */
 
-     private function readExternalBook()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset within record data
 
-         $offset = 0;
 
-         // there are 4 types of records
 
-         if (strlen($recordData) > 4) {
 
-             // external reference
 
-             // offset: 0; size: 2; number of sheet names ($nm)
 
-             $nm = self::getUInt2d($recordData, 0);
 
-             $offset += 2;
 
-             // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length)
 
-             $encodedUrlString = self::readUnicodeStringLong(substr($recordData, 2));
 
-             $offset += $encodedUrlString['size'];
 
-             // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length)
 
-             $externalSheetNames = [];
 
-             for ($i = 0; $i < $nm; ++$i) {
 
-                 $externalSheetNameString = self::readUnicodeStringLong(substr($recordData, $offset));
 
-                 $externalSheetNames[] = $externalSheetNameString['value'];
 
-                 $offset += $externalSheetNameString['size'];
 
-             }
 
-             // store the record data
 
-             $this->externalBooks[] = [
 
-                 'type' => 'external',
 
-                 'encodedUrl' => $encodedUrlString['value'],
 
-                 'externalSheetNames' => $externalSheetNames,
 
-             ];
 
-         } elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) {
 
-             // internal reference
 
-             // offset: 0; size: 2; number of sheet in this document
 
-             // offset: 2; size: 2; 0x01 0x04
 
-             $this->externalBooks[] = [
 
-                 'type' => 'internal',
 
-             ];
 
-         } elseif (substr($recordData, 0, 4) == pack('vCC', 0x0001, 0x01, 0x3A)) {
 
-             // add-in function
 
-             // offset: 0; size: 2; 0x0001
 
-             $this->externalBooks[] = [
 
-                 'type' => 'addInFunction',
 
-             ];
 
-         } elseif (substr($recordData, 0, 2) == pack('v', 0x0000)) {
 
-             // DDE links, OLE links
 
-             // offset: 0; size: 2; 0x0000
 
-             // offset: 2; size: var; encoded source document name
 
-             $this->externalBooks[] = [
 
-                 'type' => 'DDEorOLE',
 
-             ];
 
-         }
 
-     }
 
-     /**
 
-      * Read EXTERNNAME record.
 
-      */
 
-     private function readExternName()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // external sheet references provided for named cells
 
-         if ($this->version == self::XLS_BIFF8) {
 
-             // offset: 0; size: 2; options
 
-             $options = self::getUInt2d($recordData, 0);
 
-             // offset: 2; size: 2;
 
-             // offset: 4; size: 2; not used
 
-             // offset: 6; size: var
 
-             $nameString = self::readUnicodeStringShort(substr($recordData, 6));
 
-             // offset: var; size: var; formula data
 
-             $offset = 6 + $nameString['size'];
 
-             $formula = $this->getFormulaFromStructure(substr($recordData, $offset));
 
-             $this->externalNames[] = [
 
-                 'name' => $nameString['value'],
 
-                 'formula' => $formula,
 
-             ];
 
-         }
 
-     }
 
-     /**
 
-      * Read EXTERNSHEET record.
 
-      */
 
-     private function readExternSheet()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // external sheet references provided for named cells
 
-         if ($this->version == self::XLS_BIFF8) {
 
-             // offset: 0; size: 2; number of following ref structures
 
-             $nm = self::getUInt2d($recordData, 0);
 
-             for ($i = 0; $i < $nm; ++$i) {
 
-                 $this->ref[] = [
 
-                     // offset: 2 + 6 * $i; index to EXTERNALBOOK record
 
-                     'externalBookIndex' => self::getUInt2d($recordData, 2 + 6 * $i),
 
-                     // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record
 
-                     'firstSheetIndex' => self::getUInt2d($recordData, 4 + 6 * $i),
 
-                     // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record
 
-                     'lastSheetIndex' => self::getUInt2d($recordData, 6 + 6 * $i),
 
-                 ];
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * DEFINEDNAME.
 
-      *
 
-      * This record is part of a Link Table. It contains the name
 
-      * and the token array of an internal defined name. Token
 
-      * arrays of defined names contain tokens with aberrant
 
-      * token classes.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readDefinedName()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if ($this->version == self::XLS_BIFF8) {
 
-             // retrieves named cells
 
-             // offset: 0; size: 2; option flags
 
-             $opts = self::getUInt2d($recordData, 0);
 
-             // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name
 
-             $isBuiltInName = (0x0020 & $opts) >> 5;
 
-             // offset: 2; size: 1; keyboard shortcut
 
-             // offset: 3; size: 1; length of the name (character count)
 
-             $nlen = ord($recordData[3]);
 
-             // offset: 4; size: 2; size of the formula data (it can happen that this is zero)
 
-             // note: there can also be additional data, this is not included in $flen
 
-             $flen = self::getUInt2d($recordData, 4);
 
-             // offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based)
 
-             $scope = self::getUInt2d($recordData, 8);
 
-             // offset: 14; size: var; Name (Unicode string without length field)
 
-             $string = self::readUnicodeString(substr($recordData, 14), $nlen);
 
-             // offset: var; size: $flen; formula data
 
-             $offset = 14 + $string['size'];
 
-             $formulaStructure = pack('v', $flen) . substr($recordData, $offset);
 
-             try {
 
-                 $formula = $this->getFormulaFromStructure($formulaStructure);
 
-             } catch (PhpSpreadsheetException $e) {
 
-                 $formula = '';
 
-             }
 
-             $this->definedname[] = [
 
-                 'isBuiltInName' => $isBuiltInName,
 
-                 'name' => $string['value'],
 
-                 'formula' => $formula,
 
-                 'scope' => $scope,
 
-             ];
 
-         }
 
-     }
 
-     /**
 
-      * Read MSODRAWINGGROUP record.
 
-      */
 
-     private function readMsoDrawingGroup()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         // get spliced record data
 
-         $splicedRecordData = $this->getSplicedRecordData();
 
-         $recordData = $splicedRecordData['recordData'];
 
-         $this->drawingGroupData .= $recordData;
 
-     }
 
-     /**
 
-      * SST - Shared String Table.
 
-      *
 
-      * This record contains a list of all strings used anywhere
 
-      * in the workbook. Each string occurs only once. The
 
-      * workbook uses indexes into the list to reference the
 
-      * strings.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readSst()
 
-     {
 
-         // offset within (spliced) record data
 
-         $pos = 0;
 
-         // get spliced record data
 
-         $splicedRecordData = $this->getSplicedRecordData();
 
-         $recordData = $splicedRecordData['recordData'];
 
-         $spliceOffsets = $splicedRecordData['spliceOffsets'];
 
-         // offset: 0; size: 4; total number of strings in the workbook
 
-         $pos += 4;
 
-         // offset: 4; size: 4; number of following strings ($nm)
 
-         $nm = self::getInt4d($recordData, 4);
 
-         $pos += 4;
 
-         // loop through the Unicode strings (16-bit length)
 
-         for ($i = 0; $i < $nm; ++$i) {
 
-             // number of characters in the Unicode string
 
-             $numChars = self::getUInt2d($recordData, $pos);
 
-             $pos += 2;
 
-             // option flags
 
-             $optionFlags = ord($recordData[$pos]);
 
-             ++$pos;
 
-             // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed
 
-             $isCompressed = (($optionFlags & 0x01) == 0);
 
-             // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic
 
-             $hasAsian = (($optionFlags & 0x04) != 0);
 
-             // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text
 
-             $hasRichText = (($optionFlags & 0x08) != 0);
 
-             if ($hasRichText) {
 
-                 // number of Rich-Text formatting runs
 
-                 $formattingRuns = self::getUInt2d($recordData, $pos);
 
-                 $pos += 2;
 
-             }
 
-             if ($hasAsian) {
 
-                 // size of Asian phonetic setting
 
-                 $extendedRunLength = self::getInt4d($recordData, $pos);
 
-                 $pos += 4;
 
-             }
 
-             // expected byte length of character array if not split
 
-             $len = ($isCompressed) ? $numChars : $numChars * 2;
 
-             // look up limit position
 
-             foreach ($spliceOffsets as $spliceOffset) {
 
-                 // it can happen that the string is empty, therefore we need
 
-                 // <= and not just <
 
-                 if ($pos <= $spliceOffset) {
 
-                     $limitpos = $spliceOffset;
 
-                     break;
 
-                 }
 
-             }
 
-             if ($pos + $len <= $limitpos) {
 
-                 // character array is not split between records
 
-                 $retstr = substr($recordData, $pos, $len);
 
-                 $pos += $len;
 
-             } else {
 
-                 // character array is split between records
 
-                 // first part of character array
 
-                 $retstr = substr($recordData, $pos, $limitpos - $pos);
 
-                 $bytesRead = $limitpos - $pos;
 
-                 // remaining characters in Unicode string
 
-                 $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2));
 
-                 $pos = $limitpos;
 
-                 // keep reading the characters
 
-                 while ($charsLeft > 0) {
 
-                     // look up next limit position, in case the string span more than one continue record
 
-                     foreach ($spliceOffsets as $spliceOffset) {
 
-                         if ($pos < $spliceOffset) {
 
-                             $limitpos = $spliceOffset;
 
-                             break;
 
-                         }
 
-                     }
 
-                     // repeated option flags
 
-                     // OpenOffice.org documentation 5.21
 
-                     $option = ord($recordData[$pos]);
 
-                     ++$pos;
 
-                     if ($isCompressed && ($option == 0)) {
 
-                         // 1st fragment compressed
 
-                         // this fragment compressed
 
-                         $len = min($charsLeft, $limitpos - $pos);
 
-                         $retstr .= substr($recordData, $pos, $len);
 
-                         $charsLeft -= $len;
 
-                         $isCompressed = true;
 
-                     } elseif (!$isCompressed && ($option != 0)) {
 
-                         // 1st fragment uncompressed
 
-                         // this fragment uncompressed
 
-                         $len = min($charsLeft * 2, $limitpos - $pos);
 
-                         $retstr .= substr($recordData, $pos, $len);
 
-                         $charsLeft -= $len / 2;
 
-                         $isCompressed = false;
 
-                     } elseif (!$isCompressed && ($option == 0)) {
 
-                         // 1st fragment uncompressed
 
-                         // this fragment compressed
 
-                         $len = min($charsLeft, $limitpos - $pos);
 
-                         for ($j = 0; $j < $len; ++$j) {
 
-                             $retstr .= $recordData[$pos + $j]
 
-                             . chr(0);
 
-                         }
 
-                         $charsLeft -= $len;
 
-                         $isCompressed = false;
 
-                     } else {
 
-                         // 1st fragment compressed
 
-                         // this fragment uncompressed
 
-                         $newstr = '';
 
-                         $jMax = strlen($retstr);
 
-                         for ($j = 0; $j < $jMax; ++$j) {
 
-                             $newstr .= $retstr[$j] . chr(0);
 
-                         }
 
-                         $retstr = $newstr;
 
-                         $len = min($charsLeft * 2, $limitpos - $pos);
 
-                         $retstr .= substr($recordData, $pos, $len);
 
-                         $charsLeft -= $len / 2;
 
-                         $isCompressed = false;
 
-                     }
 
-                     $pos += $len;
 
-                 }
 
-             }
 
-             // convert to UTF-8
 
-             $retstr = self::encodeUTF16($retstr, $isCompressed);
 
-             // read additional Rich-Text information, if any
 
-             $fmtRuns = [];
 
-             if ($hasRichText) {
 
-                 // list of formatting runs
 
-                 for ($j = 0; $j < $formattingRuns; ++$j) {
 
-                     // first formatted character; zero-based
 
-                     $charPos = self::getUInt2d($recordData, $pos + $j * 4);
 
-                     // index to font record
 
-                     $fontIndex = self::getUInt2d($recordData, $pos + 2 + $j * 4);
 
-                     $fmtRuns[] = [
 
-                         'charPos' => $charPos,
 
-                         'fontIndex' => $fontIndex,
 
-                     ];
 
-                 }
 
-                 $pos += 4 * $formattingRuns;
 
-             }
 
-             // read additional Asian phonetics information, if any
 
-             if ($hasAsian) {
 
-                 // For Asian phonetic settings, we skip the extended string data
 
-                 $pos += $extendedRunLength;
 
-             }
 
-             // store the shared sting
 
-             $this->sst[] = [
 
-                 'value' => $retstr,
 
-                 'fmtRuns' => $fmtRuns,
 
-             ];
 
-         }
 
-         // getSplicedRecordData() takes care of moving current position in data stream
 
-     }
 
-     /**
 
-      * Read PRINTGRIDLINES record.
 
-      */
 
-     private function readPrintGridlines()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
 
-             // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines
 
-             $printGridlines = (bool) self::getUInt2d($recordData, 0);
 
-             $this->phpSheet->setPrintGridlines($printGridlines);
 
-         }
 
-     }
 
-     /**
 
-      * Read DEFAULTROWHEIGHT record.
 
-      */
 
-     private function readDefaultRowHeight()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2; option flags
 
-         // offset: 2; size: 2; default height for unused rows, (twips 1/20 point)
 
-         $height = self::getUInt2d($recordData, 2);
 
-         $this->phpSheet->getDefaultRowDimension()->setRowHeight($height / 20);
 
-     }
 
-     /**
 
-      * Read SHEETPR record.
 
-      */
 
-     private function readSheetPr()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2
 
-         // bit: 6; mask: 0x0040; 0 = outline buttons above outline group
 
-         $isSummaryBelow = (0x0040 & self::getUInt2d($recordData, 0)) >> 6;
 
-         $this->phpSheet->setShowSummaryBelow($isSummaryBelow);
 
-         // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group
 
-         $isSummaryRight = (0x0080 & self::getUInt2d($recordData, 0)) >> 7;
 
-         $this->phpSheet->setShowSummaryRight($isSummaryRight);
 
-         // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages
 
-         // this corresponds to radio button setting in page setup dialog in Excel
 
-         $this->isFitToPages = (bool) ((0x0100 & self::getUInt2d($recordData, 0)) >> 8);
 
-     }
 
-     /**
 
-      * Read HORIZONTALPAGEBREAKS record.
 
-      */
 
-     private function readHorizontalPageBreaks()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
 
-             // offset: 0; size: 2; number of the following row index structures
 
-             $nm = self::getUInt2d($recordData, 0);
 
-             // offset: 2; size: 6 * $nm; list of $nm row index structures
 
-             for ($i = 0; $i < $nm; ++$i) {
 
-                 $r = self::getUInt2d($recordData, 2 + 6 * $i);
 
-                 $cf = self::getUInt2d($recordData, 2 + 6 * $i + 2);
 
-                 $cl = self::getUInt2d($recordData, 2 + 6 * $i + 4);
 
-                 // not sure why two column indexes are necessary?
 
-                 $this->phpSheet->setBreakByColumnAndRow($cf + 1, $r, Worksheet::BREAK_ROW);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read VERTICALPAGEBREAKS record.
 
-      */
 
-     private function readVerticalPageBreaks()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
 
-             // offset: 0; size: 2; number of the following column index structures
 
-             $nm = self::getUInt2d($recordData, 0);
 
-             // offset: 2; size: 6 * $nm; list of $nm row index structures
 
-             for ($i = 0; $i < $nm; ++$i) {
 
-                 $c = self::getUInt2d($recordData, 2 + 6 * $i);
 
-                 $rf = self::getUInt2d($recordData, 2 + 6 * $i + 2);
 
-                 $rl = self::getUInt2d($recordData, 2 + 6 * $i + 4);
 
-                 // not sure why two row indexes are necessary?
 
-                 $this->phpSheet->setBreakByColumnAndRow($c + 1, $rf, Worksheet::BREAK_COLUMN);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read HEADER record.
 
-      */
 
-     private function readHeader()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: var
 
-             // realized that $recordData can be empty even when record exists
 
-             if ($recordData) {
 
-                 if ($this->version == self::XLS_BIFF8) {
 
-                     $string = self::readUnicodeStringLong($recordData);
 
-                 } else {
 
-                     $string = $this->readByteStringShort($recordData);
 
-                 }
 
-                 $this->phpSheet->getHeaderFooter()->setOddHeader($string['value']);
 
-                 $this->phpSheet->getHeaderFooter()->setEvenHeader($string['value']);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read FOOTER record.
 
-      */
 
-     private function readFooter()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: var
 
-             // realized that $recordData can be empty even when record exists
 
-             if ($recordData) {
 
-                 if ($this->version == self::XLS_BIFF8) {
 
-                     $string = self::readUnicodeStringLong($recordData);
 
-                 } else {
 
-                     $string = $this->readByteStringShort($recordData);
 
-                 }
 
-                 $this->phpSheet->getHeaderFooter()->setOddFooter($string['value']);
 
-                 $this->phpSheet->getHeaderFooter()->setEvenFooter($string['value']);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read HCENTER record.
 
-      */
 
-     private function readHcenter()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally
 
-             $isHorizontalCentered = (bool) self::getUInt2d($recordData, 0);
 
-             $this->phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered);
 
-         }
 
-     }
 
-     /**
 
-      * Read VCENTER record.
 
-      */
 
-     private function readVcenter()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered
 
-             $isVerticalCentered = (bool) self::getUInt2d($recordData, 0);
 
-             $this->phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered);
 
-         }
 
-     }
 
-     /**
 
-      * Read LEFTMARGIN record.
 
-      */
 
-     private function readLeftMargin()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 8
 
-             $this->phpSheet->getPageMargins()->setLeft(self::extractNumber($recordData));
 
-         }
 
-     }
 
-     /**
 
-      * Read RIGHTMARGIN record.
 
-      */
 
-     private function readRightMargin()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 8
 
-             $this->phpSheet->getPageMargins()->setRight(self::extractNumber($recordData));
 
-         }
 
-     }
 
-     /**
 
-      * Read TOPMARGIN record.
 
-      */
 
-     private function readTopMargin()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 8
 
-             $this->phpSheet->getPageMargins()->setTop(self::extractNumber($recordData));
 
-         }
 
-     }
 
-     /**
 
-      * Read BOTTOMMARGIN record.
 
-      */
 
-     private function readBottomMargin()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 8
 
-             $this->phpSheet->getPageMargins()->setBottom(self::extractNumber($recordData));
 
-         }
 
-     }
 
-     /**
 
-      * Read PAGESETUP record.
 
-      */
 
-     private function readPageSetup()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 2; paper size
 
-             $paperSize = self::getUInt2d($recordData, 0);
 
-             // offset: 2; size: 2; scaling factor
 
-             $scale = self::getUInt2d($recordData, 2);
 
-             // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed
 
-             $fitToWidth = self::getUInt2d($recordData, 6);
 
-             // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed
 
-             $fitToHeight = self::getUInt2d($recordData, 8);
 
-             // offset: 10; size: 2; option flags
 
-             // bit: 1; mask: 0x0002; 0=landscape, 1=portrait
 
-             $isPortrait = (0x0002 & self::getUInt2d($recordData, 10)) >> 1;
 
-             // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init
 
-             // when this bit is set, do not use flags for those properties
 
-             $isNotInit = (0x0004 & self::getUInt2d($recordData, 10)) >> 2;
 
-             if (!$isNotInit) {
 
-                 $this->phpSheet->getPageSetup()->setPaperSize($paperSize);
 
-                 switch ($isPortrait) {
 
-                     case 0:
 
-                         $this->phpSheet->getPageSetup()->setOrientation(PageSetup::ORIENTATION_LANDSCAPE);
 
-                         break;
 
-                     case 1:
 
-                         $this->phpSheet->getPageSetup()->setOrientation(PageSetup::ORIENTATION_PORTRAIT);
 
-                         break;
 
-                 }
 
-                 $this->phpSheet->getPageSetup()->setScale($scale, false);
 
-                 $this->phpSheet->getPageSetup()->setFitToPage((bool) $this->isFitToPages);
 
-                 $this->phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false);
 
-                 $this->phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false);
 
-             }
 
-             // offset: 16; size: 8; header margin (IEEE 754 floating-point value)
 
-             $marginHeader = self::extractNumber(substr($recordData, 16, 8));
 
-             $this->phpSheet->getPageMargins()->setHeader($marginHeader);
 
-             // offset: 24; size: 8; footer margin (IEEE 754 floating-point value)
 
-             $marginFooter = self::extractNumber(substr($recordData, 24, 8));
 
-             $this->phpSheet->getPageMargins()->setFooter($marginFooter);
 
-         }
 
-     }
 
-     /**
 
-      * PROTECT - Sheet protection (BIFF2 through BIFF8)
 
-      *   if this record is omitted, then it also means no sheet protection.
 
-      */
 
-     private function readProtect()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if ($this->readDataOnly) {
 
-             return;
 
-         }
 
-         // offset: 0; size: 2;
 
-         // bit 0, mask 0x01; 1 = sheet is protected
 
-         $bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0;
 
-         $this->phpSheet->getProtection()->setSheet((bool) $bool);
 
-     }
 
-     /**
 
-      * SCENPROTECT.
 
-      */
 
-     private function readScenProtect()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if ($this->readDataOnly) {
 
-             return;
 
-         }
 
-         // offset: 0; size: 2;
 
-         // bit: 0, mask 0x01; 1 = scenarios are protected
 
-         $bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0;
 
-         $this->phpSheet->getProtection()->setScenarios((bool) $bool);
 
-     }
 
-     /**
 
-      * OBJECTPROTECT.
 
-      */
 
-     private function readObjectProtect()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if ($this->readDataOnly) {
 
-             return;
 
-         }
 
-         // offset: 0; size: 2;
 
-         // bit: 0, mask 0x01; 1 = objects are protected
 
-         $bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0;
 
-         $this->phpSheet->getProtection()->setObjects((bool) $bool);
 
-     }
 
-     /**
 
-      * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8).
 
-      */
 
-     private function readPassword()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 2; 16-bit hash value of password
 
-             $password = strtoupper(dechex(self::getUInt2d($recordData, 0))); // the hashed password
 
-             $this->phpSheet->getProtection()->setPassword($password, true);
 
-         }
 
-     }
 
-     /**
 
-      * Read DEFCOLWIDTH record.
 
-      */
 
-     private function readDefColWidth()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2; default column width
 
-         $width = self::getUInt2d($recordData, 0);
 
-         if ($width != 8) {
 
-             $this->phpSheet->getDefaultColumnDimension()->setWidth($width);
 
-         }
 
-     }
 
-     /**
 
-      * Read COLINFO record.
 
-      */
 
-     private function readColInfo()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 2; index to first column in range
 
-             $firstColumnIndex = self::getUInt2d($recordData, 0);
 
-             // offset: 2; size: 2; index to last column in range
 
-             $lastColumnIndex = self::getUInt2d($recordData, 2);
 
-             // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character
 
-             $width = self::getUInt2d($recordData, 4);
 
-             // offset: 6; size: 2; index to XF record for default column formatting
 
-             $xfIndex = self::getUInt2d($recordData, 6);
 
-             // offset: 8; size: 2; option flags
 
-             // bit: 0; mask: 0x0001; 1= columns are hidden
 
-             $isHidden = (0x0001 & self::getUInt2d($recordData, 8)) >> 0;
 
-             // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline)
 
-             $level = (0x0700 & self::getUInt2d($recordData, 8)) >> 8;
 
-             // bit: 12; mask: 0x1000; 1 = collapsed
 
-             $isCollapsed = (0x1000 & self::getUInt2d($recordData, 8)) >> 12;
 
-             // offset: 10; size: 2; not used
 
-             for ($i = $firstColumnIndex + 1; $i <= $lastColumnIndex + 1; ++$i) {
 
-                 if ($lastColumnIndex == 255 || $lastColumnIndex == 256) {
 
-                     $this->phpSheet->getDefaultColumnDimension()->setWidth($width / 256);
 
-                     break;
 
-                 }
 
-                 $this->phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256);
 
-                 $this->phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden);
 
-                 $this->phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level);
 
-                 $this->phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed);
 
-                 $this->phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * ROW.
 
-      *
 
-      * This record contains the properties of a single row in a
 
-      * sheet. Rows and cells in a sheet are divided into blocks
 
-      * of 32 rows.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readRow()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 2; index of this row
 
-             $r = self::getUInt2d($recordData, 0);
 
-             // offset: 2; size: 2; index to column of the first cell which is described by a cell record
 
-             // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1
 
-             // offset: 6; size: 2;
 
-             // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point
 
-             $height = (0x7FFF & self::getUInt2d($recordData, 6)) >> 0;
 
-             // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height
 
-             $useDefaultHeight = (0x8000 & self::getUInt2d($recordData, 6)) >> 15;
 
-             if (!$useDefaultHeight) {
 
-                 $this->phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20);
 
-             }
 
-             // offset: 8; size: 2; not used
 
-             // offset: 10; size: 2; not used in BIFF5-BIFF8
 
-             // offset: 12; size: 4; option flags and default row formatting
 
-             // bit: 2-0: mask: 0x00000007; outline level of the row
 
-             $level = (0x00000007 & self::getInt4d($recordData, 12)) >> 0;
 
-             $this->phpSheet->getRowDimension($r + 1)->setOutlineLevel($level);
 
-             // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed
 
-             $isCollapsed = (0x00000010 & self::getInt4d($recordData, 12)) >> 4;
 
-             $this->phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed);
 
-             // bit: 5; mask: 0x00000020; 1 = row is hidden
 
-             $isHidden = (0x00000020 & self::getInt4d($recordData, 12)) >> 5;
 
-             $this->phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden);
 
-             // bit: 7; mask: 0x00000080; 1 = row has explicit format
 
-             $hasExplicitFormat = (0x00000080 & self::getInt4d($recordData, 12)) >> 7;
 
-             // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record
 
-             $xfIndex = (0x0FFF0000 & self::getInt4d($recordData, 12)) >> 16;
 
-             if ($hasExplicitFormat && isset($this->mapCellXfIndex[$xfIndex])) {
 
-                 $this->phpSheet->getRowDimension($r + 1)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read RK record
 
-      * This record represents a cell that contains an RK value
 
-      * (encoded integer or floating-point value). If a
 
-      * floating-point value cannot be encoded to an RK value,
 
-      * a NUMBER record will be written. This record replaces the
 
-      * record INTEGER written in BIFF2.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readRk()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2; index to row
 
-         $row = self::getUInt2d($recordData, 0);
 
-         // offset: 2; size: 2; index to column
 
-         $column = self::getUInt2d($recordData, 2);
 
-         $columnString = Coordinate::stringFromColumnIndex($column + 1);
 
-         // Read cell?
 
-         if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
 
-             // offset: 4; size: 2; index to XF record
 
-             $xfIndex = self::getUInt2d($recordData, 4);
 
-             // offset: 6; size: 4; RK value
 
-             $rknum = self::getInt4d($recordData, 6);
 
-             $numValue = self::getIEEE754($rknum);
 
-             $cell = $this->phpSheet->getCell($columnString . ($row + 1));
 
-             if (!$this->readDataOnly) {
 
-                 // add style information
 
-                 $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
 
-             }
 
-             // add cell
 
-             $cell->setValueExplicit($numValue, DataType::TYPE_NUMERIC);
 
-         }
 
-     }
 
-     /**
 
-      * Read LABELSST record
 
-      * This record represents a cell that contains a string. It
 
-      * replaces the LABEL record and RSTRING record used in
 
-      * BIFF2-BIFF5.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readLabelSst()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2; index to row
 
-         $row = self::getUInt2d($recordData, 0);
 
-         // offset: 2; size: 2; index to column
 
-         $column = self::getUInt2d($recordData, 2);
 
-         $columnString = Coordinate::stringFromColumnIndex($column + 1);
 
-         $emptyCell = true;
 
-         // Read cell?
 
-         if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
 
-             // offset: 4; size: 2; index to XF record
 
-             $xfIndex = self::getUInt2d($recordData, 4);
 
-             // offset: 6; size: 4; index to SST record
 
-             $index = self::getInt4d($recordData, 6);
 
-             // add cell
 
-             if (($fmtRuns = $this->sst[$index]['fmtRuns']) && !$this->readDataOnly) {
 
-                 // then we should treat as rich text
 
-                 $richText = new RichText();
 
-                 $charPos = 0;
 
-                 $sstCount = count($this->sst[$index]['fmtRuns']);
 
-                 for ($i = 0; $i <= $sstCount; ++$i) {
 
-                     if (isset($fmtRuns[$i])) {
 
-                         $text = StringHelper::substring($this->sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos);
 
-                         $charPos = $fmtRuns[$i]['charPos'];
 
-                     } else {
 
-                         $text = StringHelper::substring($this->sst[$index]['value'], $charPos, StringHelper::countCharacters($this->sst[$index]['value']));
 
-                     }
 
-                     if (StringHelper::countCharacters($text) > 0) {
 
-                         if ($i == 0) { // first text run, no style
 
-                             $richText->createText($text);
 
-                         } else {
 
-                             $textRun = $richText->createTextRun($text);
 
-                             if (isset($fmtRuns[$i - 1])) {
 
-                                 if ($fmtRuns[$i - 1]['fontIndex'] < 4) {
 
-                                     $fontIndex = $fmtRuns[$i - 1]['fontIndex'];
 
-                                 } else {
 
-                                     // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
 
-                                     // check the OpenOffice documentation of the FONT record
 
-                                     $fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1;
 
-                                 }
 
-                                 $textRun->setFont(clone $this->objFonts[$fontIndex]);
 
-                             }
 
-                         }
 
-                     }
 
-                 }
 
-                 if ($this->readEmptyCells || trim($richText->getPlainText()) !== '') {
 
-                     $cell = $this->phpSheet->getCell($columnString . ($row + 1));
 
-                     $cell->setValueExplicit($richText, DataType::TYPE_STRING);
 
-                     $emptyCell = false;
 
-                 }
 
-             } else {
 
-                 if ($this->readEmptyCells || trim($this->sst[$index]['value']) !== '') {
 
-                     $cell = $this->phpSheet->getCell($columnString . ($row + 1));
 
-                     $cell->setValueExplicit($this->sst[$index]['value'], DataType::TYPE_STRING);
 
-                     $emptyCell = false;
 
-                 }
 
-             }
 
-             if (!$this->readDataOnly && !$emptyCell) {
 
-                 // add style information
 
-                 $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read MULRK record
 
-      * This record represents a cell range containing RK value
 
-      * cells. All cells are located in the same row.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readMulRk()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2; index to row
 
-         $row = self::getUInt2d($recordData, 0);
 
-         // offset: 2; size: 2; index to first column
 
-         $colFirst = self::getUInt2d($recordData, 2);
 
-         // offset: var; size: 2; index to last column
 
-         $colLast = self::getUInt2d($recordData, $length - 2);
 
-         $columns = $colLast - $colFirst + 1;
 
-         // offset within record data
 
-         $offset = 4;
 
-         for ($i = 1; $i <= $columns; ++$i) {
 
-             $columnString = Coordinate::stringFromColumnIndex($colFirst + $i);
 
-             // Read cell?
 
-             if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
 
-                 // offset: var; size: 2; index to XF record
 
-                 $xfIndex = self::getUInt2d($recordData, $offset);
 
-                 // offset: var; size: 4; RK value
 
-                 $numValue = self::getIEEE754(self::getInt4d($recordData, $offset + 2));
 
-                 $cell = $this->phpSheet->getCell($columnString . ($row + 1));
 
-                 if (!$this->readDataOnly) {
 
-                     // add style
 
-                     $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
 
-                 }
 
-                 // add cell value
 
-                 $cell->setValueExplicit($numValue, DataType::TYPE_NUMERIC);
 
-             }
 
-             $offset += 6;
 
-         }
 
-     }
 
-     /**
 
-      * Read NUMBER record
 
-      * This record represents a cell that contains a
 
-      * floating-point value.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readNumber()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2; index to row
 
-         $row = self::getUInt2d($recordData, 0);
 
-         // offset: 2; size 2; index to column
 
-         $column = self::getUInt2d($recordData, 2);
 
-         $columnString = Coordinate::stringFromColumnIndex($column + 1);
 
-         // Read cell?
 
-         if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
 
-             // offset 4; size: 2; index to XF record
 
-             $xfIndex = self::getUInt2d($recordData, 4);
 
-             $numValue = self::extractNumber(substr($recordData, 6, 8));
 
-             $cell = $this->phpSheet->getCell($columnString . ($row + 1));
 
-             if (!$this->readDataOnly) {
 
-                 // add cell style
 
-                 $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
 
-             }
 
-             // add cell value
 
-             $cell->setValueExplicit($numValue, DataType::TYPE_NUMERIC);
 
-         }
 
-     }
 
-     /**
 
-      * Read FORMULA record + perhaps a following STRING record if formula result is a string
 
-      * This record contains the token array and the result of a
 
-      * formula cell.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readFormula()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2; row index
 
-         $row = self::getUInt2d($recordData, 0);
 
-         // offset: 2; size: 2; col index
 
-         $column = self::getUInt2d($recordData, 2);
 
-         $columnString = Coordinate::stringFromColumnIndex($column + 1);
 
-         // offset: 20: size: variable; formula structure
 
-         $formulaStructure = substr($recordData, 20);
 
-         // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc.
 
-         $options = self::getUInt2d($recordData, 14);
 
-         // bit: 0; mask: 0x0001; 1 = recalculate always
 
-         // bit: 1; mask: 0x0002; 1 = calculate on open
 
-         // bit: 2; mask: 0x0008; 1 = part of a shared formula
 
-         $isPartOfSharedFormula = (bool) (0x0008 & $options);
 
-         // WARNING:
 
-         // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true
 
-         // the formula data may be ordinary formula data, therefore we need to check
 
-         // explicitly for the tExp token (0x01)
 
-         $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure[2]) == 0x01;
 
-         if ($isPartOfSharedFormula) {
 
-             // part of shared formula which means there will be a formula with a tExp token and nothing else
 
-             // get the base cell, grab tExp token
 
-             $baseRow = self::getUInt2d($formulaStructure, 3);
 
-             $baseCol = self::getUInt2d($formulaStructure, 5);
 
-             $this->baseCell = Coordinate::stringFromColumnIndex($baseCol + 1) . ($baseRow + 1);
 
-         }
 
-         // Read cell?
 
-         if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
 
-             if ($isPartOfSharedFormula) {
 
-                 // formula is added to this cell after the sheet has been read
 
-                 $this->sharedFormulaParts[$columnString . ($row + 1)] = $this->baseCell;
 
-             }
 
-             // offset: 16: size: 4; not used
 
-             // offset: 4; size: 2; XF index
 
-             $xfIndex = self::getUInt2d($recordData, 4);
 
-             // offset: 6; size: 8; result of the formula
 
-             if ((ord($recordData[6]) == 0) && (ord($recordData[12]) == 255) && (ord($recordData[13]) == 255)) {
 
-                 // String formula. Result follows in appended STRING record
 
-                 $dataType = DataType::TYPE_STRING;
 
-                 // read possible SHAREDFMLA record
 
-                 $code = self::getUInt2d($this->data, $this->pos);
 
-                 if ($code == self::XLS_TYPE_SHAREDFMLA) {
 
-                     $this->readSharedFmla();
 
-                 }
 
-                 // read STRING record
 
-                 $value = $this->readString();
 
-             } elseif ((ord($recordData[6]) == 1)
 
-                 && (ord($recordData[12]) == 255)
 
-                 && (ord($recordData[13]) == 255)) {
 
-                 // Boolean formula. Result is in +2; 0=false, 1=true
 
-                 $dataType = DataType::TYPE_BOOL;
 
-                 $value = (bool) ord($recordData[8]);
 
-             } elseif ((ord($recordData[6]) == 2)
 
-                 && (ord($recordData[12]) == 255)
 
-                 && (ord($recordData[13]) == 255)) {
 
-                 // Error formula. Error code is in +2
 
-                 $dataType = DataType::TYPE_ERROR;
 
-                 $value = Xls\ErrorCode::lookup(ord($recordData[8]));
 
-             } elseif ((ord($recordData[6]) == 3)
 
-                 && (ord($recordData[12]) == 255)
 
-                 && (ord($recordData[13]) == 255)) {
 
-                 // Formula result is a null string
 
-                 $dataType = DataType::TYPE_NULL;
 
-                 $value = '';
 
-             } else {
 
-                 // forumla result is a number, first 14 bytes like _NUMBER record
 
-                 $dataType = DataType::TYPE_NUMERIC;
 
-                 $value = self::extractNumber(substr($recordData, 6, 8));
 
-             }
 
-             $cell = $this->phpSheet->getCell($columnString . ($row + 1));
 
-             if (!$this->readDataOnly) {
 
-                 // add cell style
 
-                 $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
 
-             }
 
-             // store the formula
 
-             if (!$isPartOfSharedFormula) {
 
-                 // not part of shared formula
 
-                 // add cell value. If we can read formula, populate with formula, otherwise just used cached value
 
-                 try {
 
-                     if ($this->version != self::XLS_BIFF8) {
 
-                         throw new Exception('Not BIFF8. Can only read BIFF8 formulas');
 
-                     }
 
-                     $formula = $this->getFormulaFromStructure($formulaStructure); // get formula in human language
 
-                     $cell->setValueExplicit('=' . $formula, DataType::TYPE_FORMULA);
 
-                 } catch (PhpSpreadsheetException $e) {
 
-                     $cell->setValueExplicit($value, $dataType);
 
-                 }
 
-             } else {
 
-                 if ($this->version == self::XLS_BIFF8) {
 
-                     // do nothing at this point, formula id added later in the code
 
-                 } else {
 
-                     $cell->setValueExplicit($value, $dataType);
 
-                 }
 
-             }
 
-             // store the cached calculated value
 
-             $cell->setCalculatedValue($value);
 
-         }
 
-     }
 
-     /**
 
-      * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader,
 
-      * which usually contains relative references.
 
-      * These will be used to construct the formula in each shared formula part after the sheet is read.
 
-      */
 
-     private function readSharedFmla()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything
 
-         $cellRange = substr($recordData, 0, 6);
 
-         $cellRange = $this->readBIFF5CellRangeAddressFixed($cellRange); // note: even BIFF8 uses BIFF5 syntax
 
-         // offset: 6, size: 1; not used
 
-         // offset: 7, size: 1; number of existing FORMULA records for this shared formula
 
-         $no = ord($recordData[7]);
 
-         // offset: 8, size: var; Binary token array of the shared formula
 
-         $formula = substr($recordData, 8);
 
-         // at this point we only store the shared formula for later use
 
-         $this->sharedFormulas[$this->baseCell] = $formula;
 
-     }
 
-     /**
 
-      * Read a STRING record from current stream position and advance the stream pointer to next record
 
-      * This record is used for storing result from FORMULA record when it is a string, and
 
-      * it occurs directly after the FORMULA record.
 
-      *
 
-      * @return string The string contents as UTF-8
 
-      */
 
-     private function readString()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if ($this->version == self::XLS_BIFF8) {
 
-             $string = self::readUnicodeStringLong($recordData);
 
-             $value = $string['value'];
 
-         } else {
 
-             $string = $this->readByteStringLong($recordData);
 
-             $value = $string['value'];
 
-         }
 
-         return $value;
 
-     }
 
-     /**
 
-      * Read BOOLERR record
 
-      * This record represents a Boolean value or error value
 
-      * cell.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readBoolErr()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2; row index
 
-         $row = self::getUInt2d($recordData, 0);
 
-         // offset: 2; size: 2; column index
 
-         $column = self::getUInt2d($recordData, 2);
 
-         $columnString = Coordinate::stringFromColumnIndex($column + 1);
 
-         // Read cell?
 
-         if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
 
-             // offset: 4; size: 2; index to XF record
 
-             $xfIndex = self::getUInt2d($recordData, 4);
 
-             // offset: 6; size: 1; the boolean value or error value
 
-             $boolErr = ord($recordData[6]);
 
-             // offset: 7; size: 1; 0=boolean; 1=error
 
-             $isError = ord($recordData[7]);
 
-             $cell = $this->phpSheet->getCell($columnString . ($row + 1));
 
-             switch ($isError) {
 
-                 case 0: // boolean
 
-                     $value = (bool) $boolErr;
 
-                     // add cell value
 
-                     $cell->setValueExplicit($value, DataType::TYPE_BOOL);
 
-                     break;
 
-                 case 1: // error type
 
-                     $value = Xls\ErrorCode::lookup($boolErr);
 
-                     // add cell value
 
-                     $cell->setValueExplicit($value, DataType::TYPE_ERROR);
 
-                     break;
 
-             }
 
-             if (!$this->readDataOnly) {
 
-                 // add cell style
 
-                 $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read MULBLANK record
 
-      * This record represents a cell range of empty cells. All
 
-      * cells are located in the same row.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readMulBlank()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2; index to row
 
-         $row = self::getUInt2d($recordData, 0);
 
-         // offset: 2; size: 2; index to first column
 
-         $fc = self::getUInt2d($recordData, 2);
 
-         // offset: 4; size: 2 x nc; list of indexes to XF records
 
-         // add style information
 
-         if (!$this->readDataOnly && $this->readEmptyCells) {
 
-             for ($i = 0; $i < $length / 2 - 3; ++$i) {
 
-                 $columnString = Coordinate::stringFromColumnIndex($fc + $i + 1);
 
-                 // Read cell?
 
-                 if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
 
-                     $xfIndex = self::getUInt2d($recordData, 4 + 2 * $i);
 
-                     $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
 
-                 }
 
-             }
 
-         }
 
-         // offset: 6; size 2; index to last column (not needed)
 
-     }
 
-     /**
 
-      * Read LABEL record
 
-      * This record represents a cell that contains a string. In
 
-      * BIFF8 it is usually replaced by the LABELSST record.
 
-      * Excel still uses this record, if it copies unformatted
 
-      * text cells to the clipboard.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readLabel()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2; index to row
 
-         $row = self::getUInt2d($recordData, 0);
 
-         // offset: 2; size: 2; index to column
 
-         $column = self::getUInt2d($recordData, 2);
 
-         $columnString = Coordinate::stringFromColumnIndex($column + 1);
 
-         // Read cell?
 
-         if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
 
-             // offset: 4; size: 2; XF index
 
-             $xfIndex = self::getUInt2d($recordData, 4);
 
-             // add cell value
 
-             // todo: what if string is very long? continue record
 
-             if ($this->version == self::XLS_BIFF8) {
 
-                 $string = self::readUnicodeStringLong(substr($recordData, 6));
 
-                 $value = $string['value'];
 
-             } else {
 
-                 $string = $this->readByteStringLong(substr($recordData, 6));
 
-                 $value = $string['value'];
 
-             }
 
-             if ($this->readEmptyCells || trim($value) !== '') {
 
-                 $cell = $this->phpSheet->getCell($columnString . ($row + 1));
 
-                 $cell->setValueExplicit($value, DataType::TYPE_STRING);
 
-                 if (!$this->readDataOnly) {
 
-                     // add cell style
 
-                     $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
 
-                 }
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read BLANK record.
 
-      */
 
-     private function readBlank()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2; row index
 
-         $row = self::getUInt2d($recordData, 0);
 
-         // offset: 2; size: 2; col index
 
-         $col = self::getUInt2d($recordData, 2);
 
-         $columnString = Coordinate::stringFromColumnIndex($col + 1);
 
-         // Read cell?
 
-         if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
 
-             // offset: 4; size: 2; XF index
 
-             $xfIndex = self::getUInt2d($recordData, 4);
 
-             // add style information
 
-             if (!$this->readDataOnly && $this->readEmptyCells) {
 
-                 $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read MSODRAWING record.
 
-      */
 
-     private function readMsoDrawing()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         // get spliced record data
 
-         $splicedRecordData = $this->getSplicedRecordData();
 
-         $recordData = $splicedRecordData['recordData'];
 
-         $this->drawingData .= $recordData;
 
-     }
 
-     /**
 
-      * Read OBJ record.
 
-      */
 
-     private function readObj()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if ($this->readDataOnly || $this->version != self::XLS_BIFF8) {
 
-             return;
 
-         }
 
-         // recordData consists of an array of subrecords looking like this:
 
-         //    ft: 2 bytes; ftCmo type (0x15)
 
-         //    cb: 2 bytes; size in bytes of ftCmo data
 
-         //    ot: 2 bytes; Object Type
 
-         //    id: 2 bytes; Object id number
 
-         //    grbit: 2 bytes; Option Flags
 
-         //    data: var; subrecord data
 
-         // for now, we are just interested in the second subrecord containing the object type
 
-         $ftCmoType = self::getUInt2d($recordData, 0);
 
-         $cbCmoSize = self::getUInt2d($recordData, 2);
 
-         $otObjType = self::getUInt2d($recordData, 4);
 
-         $idObjID = self::getUInt2d($recordData, 6);
 
-         $grbitOpts = self::getUInt2d($recordData, 6);
 
-         $this->objs[] = [
 
-             'ftCmoType' => $ftCmoType,
 
-             'cbCmoSize' => $cbCmoSize,
 
-             'otObjType' => $otObjType,
 
-             'idObjID' => $idObjID,
 
-             'grbitOpts' => $grbitOpts,
 
-         ];
 
-         $this->textObjRef = $idObjID;
 
-     }
 
-     /**
 
-      * Read WINDOW2 record.
 
-      */
 
-     private function readWindow2()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2; option flags
 
-         $options = self::getUInt2d($recordData, 0);
 
-         // offset: 2; size: 2; index to first visible row
 
-         $firstVisibleRow = self::getUInt2d($recordData, 2);
 
-         // offset: 4; size: 2; index to first visible colum
 
-         $firstVisibleColumn = self::getUInt2d($recordData, 4);
 
-         if ($this->version === self::XLS_BIFF8) {
 
-             // offset:  8; size: 2; not used
 
-             // offset: 10; size: 2; cached magnification factor in page break preview (in percent); 0 = Default (60%)
 
-             // offset: 12; size: 2; cached magnification factor in normal view (in percent); 0 = Default (100%)
 
-             // offset: 14; size: 4; not used
 
-             $zoomscaleInPageBreakPreview = self::getUInt2d($recordData, 10);
 
-             if ($zoomscaleInPageBreakPreview === 0) {
 
-                 $zoomscaleInPageBreakPreview = 60;
 
-             }
 
-             $zoomscaleInNormalView = self::getUInt2d($recordData, 12);
 
-             if ($zoomscaleInNormalView === 0) {
 
-                 $zoomscaleInNormalView = 100;
 
-             }
 
-         }
 
-         // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines
 
-         $showGridlines = (bool) ((0x0002 & $options) >> 1);
 
-         $this->phpSheet->setShowGridlines($showGridlines);
 
-         // bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers
 
-         $showRowColHeaders = (bool) ((0x0004 & $options) >> 2);
 
-         $this->phpSheet->setShowRowColHeaders($showRowColHeaders);
 
-         // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen
 
-         $this->frozen = (bool) ((0x0008 & $options) >> 3);
 
-         // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left
 
-         $this->phpSheet->setRightToLeft((bool) ((0x0040 & $options) >> 6));
 
-         // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active
 
-         $isActive = (bool) ((0x0400 & $options) >> 10);
 
-         if ($isActive) {
 
-             $this->spreadsheet->setActiveSheetIndex($this->spreadsheet->getIndex($this->phpSheet));
 
-         }
 
-         // bit: 11; mask: 0x0800; 0 = normal view, 1 = page break view
 
-         $isPageBreakPreview = (bool) ((0x0800 & $options) >> 11);
 
-         //FIXME: set $firstVisibleRow and $firstVisibleColumn
 
-         if ($this->phpSheet->getSheetView()->getView() !== SheetView::SHEETVIEW_PAGE_LAYOUT) {
 
-             //NOTE: this setting is inferior to page layout view(Excel2007-)
 
-             $view = $isPageBreakPreview ? SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW : SheetView::SHEETVIEW_NORMAL;
 
-             $this->phpSheet->getSheetView()->setView($view);
 
-             if ($this->version === self::XLS_BIFF8) {
 
-                 $zoomScale = $isPageBreakPreview ? $zoomscaleInPageBreakPreview : $zoomscaleInNormalView;
 
-                 $this->phpSheet->getSheetView()->setZoomScale($zoomScale);
 
-                 $this->phpSheet->getSheetView()->setZoomScaleNormal($zoomscaleInNormalView);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read PLV Record(Created by Excel2007 or upper).
 
-      */
 
-     private function readPageLayoutView()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2; rt
 
-         //->ignore
 
-         $rt = self::getUInt2d($recordData, 0);
 
-         // offset: 2; size: 2; grbitfr
 
-         //->ignore
 
-         $grbitFrt = self::getUInt2d($recordData, 2);
 
-         // offset: 4; size: 8; reserved
 
-         //->ignore
 
-         // offset: 12; size 2; zoom scale
 
-         $wScalePLV = self::getUInt2d($recordData, 12);
 
-         // offset: 14; size 2; grbit
 
-         $grbit = self::getUInt2d($recordData, 14);
 
-         // decomprise grbit
 
-         $fPageLayoutView = $grbit & 0x01;
 
-         $fRulerVisible = ($grbit >> 1) & 0x01; //no support
 
-         $fWhitespaceHidden = ($grbit >> 3) & 0x01; //no support
 
-         if ($fPageLayoutView === 1) {
 
-             $this->phpSheet->getSheetView()->setView(SheetView::SHEETVIEW_PAGE_LAYOUT);
 
-             $this->phpSheet->getSheetView()->setZoomScale($wScalePLV); //set by Excel2007 only if SHEETVIEW_PAGE_LAYOUT
 
-         }
 
-         //otherwise, we cannot know whether SHEETVIEW_PAGE_LAYOUT or SHEETVIEW_PAGE_BREAK_PREVIEW.
 
-     }
 
-     /**
 
-      * Read SCL record.
 
-      */
 
-     private function readScl()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // offset: 0; size: 2; numerator of the view magnification
 
-         $numerator = self::getUInt2d($recordData, 0);
 
-         // offset: 2; size: 2; numerator of the view magnification
 
-         $denumerator = self::getUInt2d($recordData, 2);
 
-         // set the zoom scale (in percent)
 
-         $this->phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator);
 
-     }
 
-     /**
 
-      * Read PANE record.
 
-      */
 
-     private function readPane()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 2; position of vertical split
 
-             $px = self::getUInt2d($recordData, 0);
 
-             // offset: 2; size: 2; position of horizontal split
 
-             $py = self::getUInt2d($recordData, 2);
 
-             // offset: 4; size: 2; top most visible row in the bottom pane
 
-             $rwTop = self::getUInt2d($recordData, 4);
 
-             // offset: 6; size: 2; first visible left column in the right pane
 
-             $colLeft = self::getUInt2d($recordData, 6);
 
-             if ($this->frozen) {
 
-                 // frozen panes
 
-                 $cell = Coordinate::stringFromColumnIndex($px + 1) . ($py + 1);
 
-                 $topLeftCell = Coordinate::stringFromColumnIndex($colLeft + 1) . ($rwTop + 1);
 
-                 $this->phpSheet->freezePane($cell, $topLeftCell);
 
-             }
 
-             // unfrozen panes; split windows; not supported by PhpSpreadsheet core
 
-         }
 
-     }
 
-     /**
 
-      * Read SELECTION record. There is one such record for each pane in the sheet.
 
-      */
 
-     private function readSelection()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 1; pane identifier
 
-             $paneId = ord($recordData[0]);
 
-             // offset: 1; size: 2; index to row of the active cell
 
-             $r = self::getUInt2d($recordData, 1);
 
-             // offset: 3; size: 2; index to column of the active cell
 
-             $c = self::getUInt2d($recordData, 3);
 
-             // offset: 5; size: 2; index into the following cell range list to the
 
-             //  entry that contains the active cell
 
-             $index = self::getUInt2d($recordData, 5);
 
-             // offset: 7; size: var; cell range address list containing all selected cell ranges
 
-             $data = substr($recordData, 7);
 
-             $cellRangeAddressList = $this->readBIFF5CellRangeAddressList($data); // note: also BIFF8 uses BIFF5 syntax
 
-             $selectedCells = $cellRangeAddressList['cellRangeAddresses'][0];
 
-             // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!)
 
-             if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) {
 
-                 $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells);
 
-             }
 
-             // first row '1' + last row '65536' indicates that full column is selected
 
-             if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) {
 
-                 $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells);
 
-             }
 
-             // first column 'A' + last column 'IV' indicates that full row is selected
 
-             if (preg_match('/^(A\d+\:)IV(\d+)$/', $selectedCells)) {
 
-                 $selectedCells = preg_replace('/^(A\d+\:)IV(\d+)$/', '${1}XFD${2}', $selectedCells);
 
-             }
 
-             $this->phpSheet->setSelectedCells($selectedCells);
 
-         }
 
-     }
 
-     private function includeCellRangeFiltered($cellRangeAddress)
 
-     {
 
-         $includeCellRange = true;
 
-         if ($this->getReadFilter() !== null) {
 
-             $includeCellRange = false;
 
-             $rangeBoundaries = Coordinate::getRangeBoundaries($cellRangeAddress);
 
-             ++$rangeBoundaries[1][0];
 
-             for ($row = $rangeBoundaries[0][1]; $row <= $rangeBoundaries[1][1]; ++$row) {
 
-                 for ($column = $rangeBoundaries[0][0]; $column != $rangeBoundaries[1][0]; ++$column) {
 
-                     if ($this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
 
-                         $includeCellRange = true;
 
-                         break 2;
 
-                     }
 
-                 }
 
-             }
 
-         }
 
-         return $includeCellRange;
 
-     }
 
-     /**
 
-      * MERGEDCELLS.
 
-      *
 
-      * This record contains the addresses of merged cell ranges
 
-      * in the current sheet.
 
-      *
 
-      * --    "OpenOffice.org's Documentation of the Microsoft
 
-      *         Excel File Format"
 
-      */
 
-     private function readMergedCells()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
 
-             $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($recordData);
 
-             foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) {
 
-                 if ((strpos($cellRangeAddress, ':') !== false) &&
 
-                     ($this->includeCellRangeFiltered($cellRangeAddress))) {
 
-                     $this->phpSheet->mergeCells($cellRangeAddress);
 
-                 }
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read HYPERLINK record.
 
-      */
 
-     private function readHyperLink()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer forward to next record
 
-         $this->pos += 4 + $length;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 8; cell range address of all cells containing this hyperlink
 
-             try {
 
-                 $cellRange = $this->readBIFF8CellRangeAddressFixed($recordData);
 
-             } catch (PhpSpreadsheetException $e) {
 
-                 return;
 
-             }
 
-             // offset: 8, size: 16; GUID of StdLink
 
-             // offset: 24, size: 4; unknown value
 
-             // offset: 28, size: 4; option flags
 
-             // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL
 
-             $isFileLinkOrUrl = (0x00000001 & self::getUInt2d($recordData, 28)) >> 0;
 
-             // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL
 
-             $isAbsPathOrUrl = (0x00000001 & self::getUInt2d($recordData, 28)) >> 1;
 
-             // bit: 2 (and 4); mask: 0x00000014; 0 = no description
 
-             $hasDesc = (0x00000014 & self::getUInt2d($recordData, 28)) >> 2;
 
-             // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text
 
-             $hasText = (0x00000008 & self::getUInt2d($recordData, 28)) >> 3;
 
-             // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame
 
-             $hasFrame = (0x00000080 & self::getUInt2d($recordData, 28)) >> 7;
 
-             // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name)
 
-             $isUNC = (0x00000100 & self::getUInt2d($recordData, 28)) >> 8;
 
-             // offset within record data
 
-             $offset = 32;
 
-             if ($hasDesc) {
 
-                 // offset: 32; size: var; character count of description text
 
-                 $dl = self::getInt4d($recordData, 32);
 
-                 // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated
 
-                 $desc = self::encodeUTF16(substr($recordData, 36, 2 * ($dl - 1)), false);
 
-                 $offset += 4 + 2 * $dl;
 
-             }
 
-             if ($hasFrame) {
 
-                 $fl = self::getInt4d($recordData, $offset);
 
-                 $offset += 4 + 2 * $fl;
 
-             }
 
-             // detect type of hyperlink (there are 4 types)
 
-             $hyperlinkType = null;
 
-             if ($isUNC) {
 
-                 $hyperlinkType = 'UNC';
 
-             } elseif (!$isFileLinkOrUrl) {
 
-                 $hyperlinkType = 'workbook';
 
-             } elseif (ord($recordData[$offset]) == 0x03) {
 
-                 $hyperlinkType = 'local';
 
-             } elseif (ord($recordData[$offset]) == 0xE0) {
 
-                 $hyperlinkType = 'URL';
 
-             }
 
-             switch ($hyperlinkType) {
 
-                 case 'URL':
 
-                     // section 5.58.2: Hyperlink containing a URL
 
-                     // e.g. http://example.org/index.php
 
-                     // offset: var; size: 16; GUID of URL Moniker
 
-                     $offset += 16;
 
-                     // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word
 
-                     $us = self::getInt4d($recordData, $offset);
 
-                     $offset += 4;
 
-                     // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated
 
-                     $url = self::encodeUTF16(substr($recordData, $offset, $us - 2), false);
 
-                     $nullOffset = strpos($url, chr(0x00));
 
-                     if ($nullOffset) {
 
-                         $url = substr($url, 0, $nullOffset);
 
-                     }
 
-                     $url .= $hasText ? '#' : '';
 
-                     $offset += $us;
 
-                     break;
 
-                 case 'local':
 
-                     // section 5.58.3: Hyperlink to local file
 
-                     // examples:
 
-                     //   mydoc.txt
 
-                     //   ../../somedoc.xls#Sheet!A1
 
-                     // offset: var; size: 16; GUI of File Moniker
 
-                     $offset += 16;
 
-                     // offset: var; size: 2; directory up-level count.
 
-                     $upLevelCount = self::getUInt2d($recordData, $offset);
 
-                     $offset += 2;
 
-                     // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word
 
-                     $sl = self::getInt4d($recordData, $offset);
 
-                     $offset += 4;
 
-                     // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string)
 
-                     $shortenedFilePath = substr($recordData, $offset, $sl);
 
-                     $shortenedFilePath = self::encodeUTF16($shortenedFilePath, true);
 
-                     $shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero
 
-                     $offset += $sl;
 
-                     // offset: var; size: 24; unknown sequence
 
-                     $offset += 24;
 
-                     // extended file path
 
-                     // offset: var; size: 4; size of the following file link field including string lenth mark
 
-                     $sz = self::getInt4d($recordData, $offset);
 
-                     $offset += 4;
 
-                     // only present if $sz > 0
 
-                     if ($sz > 0) {
 
-                         // offset: var; size: 4; size of the character array of the extended file path and name
 
-                         $xl = self::getInt4d($recordData, $offset);
 
-                         $offset += 4;
 
-                         // offset: var; size 2; unknown
 
-                         $offset += 2;
 
-                         // offset: var; size $xl; character array of the extended file path and name.
 
-                         $extendedFilePath = substr($recordData, $offset, $xl);
 
-                         $extendedFilePath = self::encodeUTF16($extendedFilePath, false);
 
-                         $offset += $xl;
 
-                     }
 
-                     // construct the path
 
-                     $url = str_repeat('..\\', $upLevelCount);
 
-                     $url .= ($sz > 0) ? $extendedFilePath : $shortenedFilePath; // use extended path if available
 
-                     $url .= $hasText ? '#' : '';
 
-                     break;
 
-                 case 'UNC':
 
-                     // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path
 
-                     // todo: implement
 
-                     return;
 
-                 case 'workbook':
 
-                     // section 5.58.5: Hyperlink to the Current Workbook
 
-                     // e.g. Sheet2!B1:C2, stored in text mark field
 
-                     $url = 'sheet://';
 
-                     break;
 
-                 default:
 
-                     return;
 
-             }
 
-             if ($hasText) {
 
-                 // offset: var; size: 4; character count of text mark including trailing zero word
 
-                 $tl = self::getInt4d($recordData, $offset);
 
-                 $offset += 4;
 
-                 // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated
 
-                 $text = self::encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false);
 
-                 $url .= $text;
 
-             }
 
-             // apply the hyperlink to all the relevant cells
 
-             foreach (Coordinate::extractAllCellReferencesInRange($cellRange) as $coordinate) {
 
-                 $this->phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read DATAVALIDATIONS record.
 
-      */
 
-     private function readDataValidations()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer forward to next record
 
-         $this->pos += 4 + $length;
 
-     }
 
-     /**
 
-      * Read DATAVALIDATION record.
 
-      */
 
-     private function readDataValidation()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer forward to next record
 
-         $this->pos += 4 + $length;
 
-         if ($this->readDataOnly) {
 
-             return;
 
-         }
 
-         // offset: 0; size: 4; Options
 
-         $options = self::getInt4d($recordData, 0);
 
-         // bit: 0-3; mask: 0x0000000F; type
 
-         $type = (0x0000000F & $options) >> 0;
 
-         switch ($type) {
 
-             case 0x00:
 
-                 $type = DataValidation::TYPE_NONE;
 
-                 break;
 
-             case 0x01:
 
-                 $type = DataValidation::TYPE_WHOLE;
 
-                 break;
 
-             case 0x02:
 
-                 $type = DataValidation::TYPE_DECIMAL;
 
-                 break;
 
-             case 0x03:
 
-                 $type = DataValidation::TYPE_LIST;
 
-                 break;
 
-             case 0x04:
 
-                 $type = DataValidation::TYPE_DATE;
 
-                 break;
 
-             case 0x05:
 
-                 $type = DataValidation::TYPE_TIME;
 
-                 break;
 
-             case 0x06:
 
-                 $type = DataValidation::TYPE_TEXTLENGTH;
 
-                 break;
 
-             case 0x07:
 
-                 $type = DataValidation::TYPE_CUSTOM;
 
-                 break;
 
-         }
 
-         // bit: 4-6; mask: 0x00000070; error type
 
-         $errorStyle = (0x00000070 & $options) >> 4;
 
-         switch ($errorStyle) {
 
-             case 0x00:
 
-                 $errorStyle = DataValidation::STYLE_STOP;
 
-                 break;
 
-             case 0x01:
 
-                 $errorStyle = DataValidation::STYLE_WARNING;
 
-                 break;
 
-             case 0x02:
 
-                 $errorStyle = DataValidation::STYLE_INFORMATION;
 
-                 break;
 
-         }
 
-         // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list)
 
-         // I have only seen cases where this is 1
 
-         $explicitFormula = (0x00000080 & $options) >> 7;
 
-         // bit: 8; mask: 0x00000100; 1= empty cells allowed
 
-         $allowBlank = (0x00000100 & $options) >> 8;
 
-         // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity
 
-         $suppressDropDown = (0x00000200 & $options) >> 9;
 
-         // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected
 
-         $showInputMessage = (0x00040000 & $options) >> 18;
 
-         // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered
 
-         $showErrorMessage = (0x00080000 & $options) >> 19;
 
-         // bit: 20-23; mask: 0x00F00000; condition operator
 
-         $operator = (0x00F00000 & $options) >> 20;
 
-         switch ($operator) {
 
-             case 0x00:
 
-                 $operator = DataValidation::OPERATOR_BETWEEN;
 
-                 break;
 
-             case 0x01:
 
-                 $operator = DataValidation::OPERATOR_NOTBETWEEN;
 
-                 break;
 
-             case 0x02:
 
-                 $operator = DataValidation::OPERATOR_EQUAL;
 
-                 break;
 
-             case 0x03:
 
-                 $operator = DataValidation::OPERATOR_NOTEQUAL;
 
-                 break;
 
-             case 0x04:
 
-                 $operator = DataValidation::OPERATOR_GREATERTHAN;
 
-                 break;
 
-             case 0x05:
 
-                 $operator = DataValidation::OPERATOR_LESSTHAN;
 
-                 break;
 
-             case 0x06:
 
-                 $operator = DataValidation::OPERATOR_GREATERTHANOREQUAL;
 
-                 break;
 
-             case 0x07:
 
-                 $operator = DataValidation::OPERATOR_LESSTHANOREQUAL;
 
-                 break;
 
-         }
 
-         // offset: 4; size: var; title of the prompt box
 
-         $offset = 4;
 
-         $string = self::readUnicodeStringLong(substr($recordData, $offset));
 
-         $promptTitle = $string['value'] !== chr(0) ? $string['value'] : '';
 
-         $offset += $string['size'];
 
-         // offset: var; size: var; title of the error box
 
-         $string = self::readUnicodeStringLong(substr($recordData, $offset));
 
-         $errorTitle = $string['value'] !== chr(0) ? $string['value'] : '';
 
-         $offset += $string['size'];
 
-         // offset: var; size: var; text of the prompt box
 
-         $string = self::readUnicodeStringLong(substr($recordData, $offset));
 
-         $prompt = $string['value'] !== chr(0) ? $string['value'] : '';
 
-         $offset += $string['size'];
 
-         // offset: var; size: var; text of the error box
 
-         $string = self::readUnicodeStringLong(substr($recordData, $offset));
 
-         $error = $string['value'] !== chr(0) ? $string['value'] : '';
 
-         $offset += $string['size'];
 
-         // offset: var; size: 2; size of the formula data for the first condition
 
-         $sz1 = self::getUInt2d($recordData, $offset);
 
-         $offset += 2;
 
-         // offset: var; size: 2; not used
 
-         $offset += 2;
 
-         // offset: var; size: $sz1; formula data for first condition (without size field)
 
-         $formula1 = substr($recordData, $offset, $sz1);
 
-         $formula1 = pack('v', $sz1) . $formula1; // prepend the length
 
-         try {
 
-             $formula1 = $this->getFormulaFromStructure($formula1);
 
-             // in list type validity, null characters are used as item separators
 
-             if ($type == DataValidation::TYPE_LIST) {
 
-                 $formula1 = str_replace(chr(0), ',', $formula1);
 
-             }
 
-         } catch (PhpSpreadsheetException $e) {
 
-             return;
 
-         }
 
-         $offset += $sz1;
 
-         // offset: var; size: 2; size of the formula data for the first condition
 
-         $sz2 = self::getUInt2d($recordData, $offset);
 
-         $offset += 2;
 
-         // offset: var; size: 2; not used
 
-         $offset += 2;
 
-         // offset: var; size: $sz2; formula data for second condition (without size field)
 
-         $formula2 = substr($recordData, $offset, $sz2);
 
-         $formula2 = pack('v', $sz2) . $formula2; // prepend the length
 
-         try {
 
-             $formula2 = $this->getFormulaFromStructure($formula2);
 
-         } catch (PhpSpreadsheetException $e) {
 
-             return;
 
-         }
 
-         $offset += $sz2;
 
-         // offset: var; size: var; cell range address list with
 
-         $cellRangeAddressList = $this->readBIFF8CellRangeAddressList(substr($recordData, $offset));
 
-         $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
 
-         foreach ($cellRangeAddresses as $cellRange) {
 
-             $stRange = $this->phpSheet->shrinkRangeToFit($cellRange);
 
-             foreach (Coordinate::extractAllCellReferencesInRange($stRange) as $coordinate) {
 
-                 $objValidation = $this->phpSheet->getCell($coordinate)->getDataValidation();
 
-                 $objValidation->setType($type);
 
-                 $objValidation->setErrorStyle($errorStyle);
 
-                 $objValidation->setAllowBlank((bool) $allowBlank);
 
-                 $objValidation->setShowInputMessage((bool) $showInputMessage);
 
-                 $objValidation->setShowErrorMessage((bool) $showErrorMessage);
 
-                 $objValidation->setShowDropDown(!$suppressDropDown);
 
-                 $objValidation->setOperator($operator);
 
-                 $objValidation->setErrorTitle($errorTitle);
 
-                 $objValidation->setError($error);
 
-                 $objValidation->setPromptTitle($promptTitle);
 
-                 $objValidation->setPrompt($prompt);
 
-                 $objValidation->setFormula1($formula1);
 
-                 $objValidation->setFormula2($formula2);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read SHEETLAYOUT record. Stores sheet tab color information.
 
-      */
 
-     private function readSheetLayout()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // local pointer in record data
 
-         $offset = 0;
 
-         if (!$this->readDataOnly) {
 
-             // offset: 0; size: 2; repeated record identifier 0x0862
 
-             // offset: 2; size: 10; not used
 
-             // offset: 12; size: 4; size of record data
 
-             // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?)
 
-             $sz = self::getInt4d($recordData, 12);
 
-             switch ($sz) {
 
-                 case 0x14:
 
-                     // offset: 16; size: 2; color index for sheet tab
 
-                     $colorIndex = self::getUInt2d($recordData, 16);
 
-                     $color = Xls\Color::map($colorIndex, $this->palette, $this->version);
 
-                     $this->phpSheet->getTabColor()->setRGB($color['rgb']);
 
-                     break;
 
-                 case 0x28:
 
-                     // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007
 
-                     return;
 
-                     break;
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read SHEETPROTECTION record (FEATHEADR).
 
-      */
 
-     private function readSheetProtection()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         if ($this->readDataOnly) {
 
-             return;
 
-         }
 
-         // offset: 0; size: 2; repeated record header
 
-         // offset: 2; size: 2; FRT cell reference flag (=0 currently)
 
-         // offset: 4; size: 8; Currently not used and set to 0
 
-         // offset: 12; size: 2; Shared feature type index (2=Enhanced Protetion, 4=SmartTag)
 
-         $isf = self::getUInt2d($recordData, 12);
 
-         if ($isf != 2) {
 
-             return;
 
-         }
 
-         // offset: 14; size: 1; =1 since this is a feat header
 
-         // offset: 15; size: 4; size of rgbHdrSData
 
-         // rgbHdrSData, assume "Enhanced Protection"
 
-         // offset: 19; size: 2; option flags
 
-         $options = self::getUInt2d($recordData, 19);
 
-         // bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects
 
-         $bool = (0x0001 & $options) >> 0;
 
-         $this->phpSheet->getProtection()->setObjects(!$bool);
 
-         // bit: 1; mask 0x0002; edit scenarios
 
-         $bool = (0x0002 & $options) >> 1;
 
-         $this->phpSheet->getProtection()->setScenarios(!$bool);
 
-         // bit: 2; mask 0x0004; format cells
 
-         $bool = (0x0004 & $options) >> 2;
 
-         $this->phpSheet->getProtection()->setFormatCells(!$bool);
 
-         // bit: 3; mask 0x0008; format columns
 
-         $bool = (0x0008 & $options) >> 3;
 
-         $this->phpSheet->getProtection()->setFormatColumns(!$bool);
 
-         // bit: 4; mask 0x0010; format rows
 
-         $bool = (0x0010 & $options) >> 4;
 
-         $this->phpSheet->getProtection()->setFormatRows(!$bool);
 
-         // bit: 5; mask 0x0020; insert columns
 
-         $bool = (0x0020 & $options) >> 5;
 
-         $this->phpSheet->getProtection()->setInsertColumns(!$bool);
 
-         // bit: 6; mask 0x0040; insert rows
 
-         $bool = (0x0040 & $options) >> 6;
 
-         $this->phpSheet->getProtection()->setInsertRows(!$bool);
 
-         // bit: 7; mask 0x0080; insert hyperlinks
 
-         $bool = (0x0080 & $options) >> 7;
 
-         $this->phpSheet->getProtection()->setInsertHyperlinks(!$bool);
 
-         // bit: 8; mask 0x0100; delete columns
 
-         $bool = (0x0100 & $options) >> 8;
 
-         $this->phpSheet->getProtection()->setDeleteColumns(!$bool);
 
-         // bit: 9; mask 0x0200; delete rows
 
-         $bool = (0x0200 & $options) >> 9;
 
-         $this->phpSheet->getProtection()->setDeleteRows(!$bool);
 
-         // bit: 10; mask 0x0400; select locked cells
 
-         $bool = (0x0400 & $options) >> 10;
 
-         $this->phpSheet->getProtection()->setSelectLockedCells(!$bool);
 
-         // bit: 11; mask 0x0800; sort cell range
 
-         $bool = (0x0800 & $options) >> 11;
 
-         $this->phpSheet->getProtection()->setSort(!$bool);
 
-         // bit: 12; mask 0x1000; auto filter
 
-         $bool = (0x1000 & $options) >> 12;
 
-         $this->phpSheet->getProtection()->setAutoFilter(!$bool);
 
-         // bit: 13; mask 0x2000; pivot tables
 
-         $bool = (0x2000 & $options) >> 13;
 
-         $this->phpSheet->getProtection()->setPivotTables(!$bool);
 
-         // bit: 14; mask 0x4000; select unlocked cells
 
-         $bool = (0x4000 & $options) >> 14;
 
-         $this->phpSheet->getProtection()->setSelectUnlockedCells(!$bool);
 
-         // offset: 21; size: 2; not used
 
-     }
 
-     /**
 
-      * Read RANGEPROTECTION record
 
-      * Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification,
 
-      * where it is referred to as FEAT record.
 
-      */
 
-     private function readRangeProtection()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-         // local pointer in record data
 
-         $offset = 0;
 
-         if (!$this->readDataOnly) {
 
-             $offset += 12;
 
-             // offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag
 
-             $isf = self::getUInt2d($recordData, 12);
 
-             if ($isf != 2) {
 
-                 // we only read FEAT records of type 2
 
-                 return;
 
-             }
 
-             $offset += 2;
 
-             $offset += 5;
 
-             // offset: 19; size: 2; count of ref ranges this feature is on
 
-             $cref = self::getUInt2d($recordData, 19);
 
-             $offset += 2;
 
-             $offset += 6;
 
-             // offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record)
 
-             $cellRanges = [];
 
-             for ($i = 0; $i < $cref; ++$i) {
 
-                 try {
 
-                     $cellRange = $this->readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8));
 
-                 } catch (PhpSpreadsheetException $e) {
 
-                     return;
 
-                 }
 
-                 $cellRanges[] = $cellRange;
 
-                 $offset += 8;
 
-             }
 
-             // offset: var; size: var; variable length of feature specific data
 
-             $rgbFeat = substr($recordData, $offset);
 
-             $offset += 4;
 
-             // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit)
 
-             $wPassword = self::getInt4d($recordData, $offset);
 
-             $offset += 4;
 
-             // Apply range protection to sheet
 
-             if ($cellRanges) {
 
-                 $this->phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record
 
-      * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented.
 
-      * In this case, we must treat the CONTINUE record as a MSODRAWING record.
 
-      */
 
-     private function readContinue()
 
-     {
 
-         $length = self::getUInt2d($this->data, $this->pos + 2);
 
-         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
-         // check if we are reading drawing data
 
-         // this is in case a free CONTINUE record occurs in other circumstances we are unaware of
 
-         if ($this->drawingData == '') {
 
-             // move stream pointer to next record
 
-             $this->pos += 4 + $length;
 
-             return;
 
-         }
 
-         // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data
 
-         if ($length < 4) {
 
-             // move stream pointer to next record
 
-             $this->pos += 4 + $length;
 
-             return;
 
-         }
 
-         // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record
 
-         // look inside CONTINUE record to see if it looks like a part of an Escher stream
 
-         // we know that Escher stream may be split at least at
 
-         //        0xF003 MsofbtSpgrContainer
 
-         //        0xF004 MsofbtSpContainer
 
-         //        0xF00D MsofbtClientTextbox
 
-         $validSplitPoints = [0xF003, 0xF004, 0xF00D]; // add identifiers if we find more
 
-         $splitPoint = self::getUInt2d($recordData, 2);
 
-         if (in_array($splitPoint, $validSplitPoints)) {
 
-             // get spliced record data (and move pointer to next record)
 
-             $splicedRecordData = $this->getSplicedRecordData();
 
-             $this->drawingData .= $splicedRecordData['recordData'];
 
-             return;
 
-         }
 
-         // move stream pointer to next record
 
-         $this->pos += 4 + $length;
 
-     }
 
-     /**
 
-      * Reads a record from current position in data stream and continues reading data as long as CONTINUE
 
-      * records are found. Splices the record data pieces and returns the combined string as if record data
 
-      * is in one piece.
 
-      * Moves to next current position in data stream to start of next record different from a CONtINUE record.
 
-      *
 
-      * @return array
 
-      */
 
-     private function getSplicedRecordData()
 
-     {
 
-         $data = '';
 
-         $spliceOffsets = [];
 
-         $i = 0;
 
-         $spliceOffsets[0] = 0;
 
-         do {
 
-             ++$i;
 
-             // offset: 0; size: 2; identifier
 
-             $identifier = self::getUInt2d($this->data, $this->pos);
 
-             // offset: 2; size: 2; length
 
-             $length = self::getUInt2d($this->data, $this->pos + 2);
 
-             $data .= $this->readRecordData($this->data, $this->pos + 4, $length);
 
-             $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length;
 
-             $this->pos += 4 + $length;
 
-             $nextIdentifier = self::getUInt2d($this->data, $this->pos);
 
-         } while ($nextIdentifier == self::XLS_TYPE_CONTINUE);
 
-         $splicedData = [
 
-             'recordData' => $data,
 
-             'spliceOffsets' => $spliceOffsets,
 
-         ];
 
-         return $splicedData;
 
-     }
 
-     /**
 
-      * Convert formula structure into human readable Excel formula like 'A3+A5*5'.
 
-      *
 
-      * @param string $formulaStructure The complete binary data for the formula
 
-      * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
 
-      *
 
-      * @return string Human readable formula
 
-      */
 
-     private function getFormulaFromStructure($formulaStructure, $baseCell = 'A1')
 
-     {
 
-         // offset: 0; size: 2; size of the following formula data
 
-         $sz = self::getUInt2d($formulaStructure, 0);
 
-         // offset: 2; size: sz
 
-         $formulaData = substr($formulaStructure, 2, $sz);
 
-         // offset: 2 + sz; size: variable (optional)
 
-         if (strlen($formulaStructure) > 2 + $sz) {
 
-             $additionalData = substr($formulaStructure, 2 + $sz);
 
-         } else {
 
-             $additionalData = '';
 
-         }
 
-         return $this->getFormulaFromData($formulaData, $additionalData, $baseCell);
 
-     }
 
-     /**
 
-      * Take formula data and additional data for formula and return human readable formula.
 
-      *
 
-      * @param string $formulaData The binary data for the formula itself
 
-      * @param string $additionalData Additional binary data going with the formula
 
-      * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
 
-      *
 
-      * @return string Human readable formula
 
-      */
 
-     private function getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1')
 
-     {
 
-         // start parsing the formula data
 
-         $tokens = [];
 
-         while (strlen($formulaData) > 0 and $token = $this->getNextToken($formulaData, $baseCell)) {
 
-             $tokens[] = $token;
 
-             $formulaData = substr($formulaData, $token['size']);
 
-         }
 
-         $formulaString = $this->createFormulaFromTokens($tokens, $additionalData);
 
-         return $formulaString;
 
-     }
 
-     /**
 
-      * Take array of tokens together with additional data for formula and return human readable formula.
 
-      *
 
-      * @param array $tokens
 
-      * @param string $additionalData Additional binary data going with the formula
 
-      *
 
-      * @return string Human readable formula
 
-      */
 
-     private function createFormulaFromTokens($tokens, $additionalData)
 
-     {
 
-         // empty formula?
 
-         if (empty($tokens)) {
 
-             return '';
 
-         }
 
-         $formulaStrings = [];
 
-         foreach ($tokens as $token) {
 
-             // initialize spaces
 
-             $space0 = isset($space0) ? $space0 : ''; // spaces before next token, not tParen
 
-             $space1 = isset($space1) ? $space1 : ''; // carriage returns before next token, not tParen
 
-             $space2 = isset($space2) ? $space2 : ''; // spaces before opening parenthesis
 
-             $space3 = isset($space3) ? $space3 : ''; // carriage returns before opening parenthesis
 
-             $space4 = isset($space4) ? $space4 : ''; // spaces before closing parenthesis
 
-             $space5 = isset($space5) ? $space5 : ''; // carriage returns before closing parenthesis
 
-             switch ($token['name']) {
 
-                 case 'tAdd': // addition
 
-                 case 'tConcat': // addition
 
-                 case 'tDiv': // division
 
-                 case 'tEQ': // equality
 
-                 case 'tGE': // greater than or equal
 
-                 case 'tGT': // greater than
 
-                 case 'tIsect': // intersection
 
-                 case 'tLE': // less than or equal
 
-                 case 'tList': // less than or equal
 
-                 case 'tLT': // less than
 
-                 case 'tMul': // multiplication
 
-                 case 'tNE': // multiplication
 
-                 case 'tPower': // power
 
-                 case 'tRange': // range
 
-                 case 'tSub': // subtraction
 
-                     $op2 = array_pop($formulaStrings);
 
-                     $op1 = array_pop($formulaStrings);
 
-                     $formulaStrings[] = "$op1$space1$space0{$token['data']}$op2";
 
-                     unset($space0, $space1);
 
-                     break;
 
-                 case 'tUplus': // unary plus
 
-                 case 'tUminus': // unary minus
 
-                     $op = array_pop($formulaStrings);
 
-                     $formulaStrings[] = "$space1$space0{$token['data']}$op";
 
-                     unset($space0, $space1);
 
-                     break;
 
-                 case 'tPercent': // percent sign
 
-                     $op = array_pop($formulaStrings);
 
-                     $formulaStrings[] = "$op$space1$space0{$token['data']}";
 
-                     unset($space0, $space1);
 
-                     break;
 
-                 case 'tAttrVolatile': // indicates volatile function
 
-                 case 'tAttrIf':
 
-                 case 'tAttrSkip':
 
-                 case 'tAttrChoose':
 
-                     // token is only important for Excel formula evaluator
 
-                     // do nothing
 
-                     break;
 
-                 case 'tAttrSpace': // space / carriage return
 
-                     // space will be used when next token arrives, do not alter formulaString stack
 
-                     switch ($token['data']['spacetype']) {
 
-                         case 'type0':
 
-                             $space0 = str_repeat(' ', $token['data']['spacecount']);
 
-                             break;
 
-                         case 'type1':
 
-                             $space1 = str_repeat("\n", $token['data']['spacecount']);
 
-                             break;
 
-                         case 'type2':
 
-                             $space2 = str_repeat(' ', $token['data']['spacecount']);
 
-                             break;
 
-                         case 'type3':
 
-                             $space3 = str_repeat("\n", $token['data']['spacecount']);
 
-                             break;
 
-                         case 'type4':
 
-                             $space4 = str_repeat(' ', $token['data']['spacecount']);
 
-                             break;
 
-                         case 'type5':
 
-                             $space5 = str_repeat("\n", $token['data']['spacecount']);
 
-                             break;
 
-                     }
 
-                     break;
 
-                 case 'tAttrSum': // SUM function with one parameter
 
-                     $op = array_pop($formulaStrings);
 
-                     $formulaStrings[] = "{$space1}{$space0}SUM($op)";
 
-                     unset($space0, $space1);
 
-                     break;
 
-                 case 'tFunc': // function with fixed number of arguments
 
-                 case 'tFuncV': // function with variable number of arguments
 
-                     if ($token['data']['function'] != '') {
 
-                         // normal function
 
-                         $ops = []; // array of operators
 
-                         for ($i = 0; $i < $token['data']['args']; ++$i) {
 
-                             $ops[] = array_pop($formulaStrings);
 
-                         }
 
-                         $ops = array_reverse($ops);
 
-                         $formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ')';
 
-                         unset($space0, $space1);
 
-                     } else {
 
-                         // add-in function
 
-                         $ops = []; // array of operators
 
-                         for ($i = 0; $i < $token['data']['args'] - 1; ++$i) {
 
-                             $ops[] = array_pop($formulaStrings);
 
-                         }
 
-                         $ops = array_reverse($ops);
 
-                         $function = array_pop($formulaStrings);
 
-                         $formulaStrings[] = "$space1$space0$function(" . implode(',', $ops) . ')';
 
-                         unset($space0, $space1);
 
-                     }
 
-                     break;
 
-                 case 'tParen': // parenthesis
 
-                     $expression = array_pop($formulaStrings);
 
-                     $formulaStrings[] = "$space3$space2($expression$space5$space4)";
 
-                     unset($space2, $space3, $space4, $space5);
 
-                     break;
 
-                 case 'tArray': // array constant
 
-                     $constantArray = self::readBIFF8ConstantArray($additionalData);
 
-                     $formulaStrings[] = $space1 . $space0 . $constantArray['value'];
 
-                     $additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data
 
-                     unset($space0, $space1);
 
-                     break;
 
-                 case 'tMemArea':
 
-                     // bite off chunk of additional data
 
-                     $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($additionalData);
 
-                     $additionalData = substr($additionalData, $cellRangeAddressList['size']);
 
-                     $formulaStrings[] = "$space1$space0{$token['data']}";
 
-                     unset($space0, $space1);
 
-                     break;
 
-                 case 'tArea': // cell range address
 
-                 case 'tBool': // boolean
 
-                 case 'tErr': // error code
 
-                 case 'tInt': // integer
 
-                 case 'tMemErr':
 
-                 case 'tMemFunc':
 
-                 case 'tMissArg':
 
-                 case 'tName':
 
-                 case 'tNameX':
 
-                 case 'tNum': // number
 
-                 case 'tRef': // single cell reference
 
-                 case 'tRef3d': // 3d cell reference
 
-                 case 'tArea3d': // 3d cell range reference
 
-                 case 'tRefN':
 
-                 case 'tAreaN':
 
-                 case 'tStr': // string
 
-                     $formulaStrings[] = "$space1$space0{$token['data']}";
 
-                     unset($space0, $space1);
 
-                     break;
 
-             }
 
-         }
 
-         $formulaString = $formulaStrings[0];
 
-         return $formulaString;
 
-     }
 
-     /**
 
-      * Fetch next token from binary formula data.
 
-      *
 
-      * @param string $formulaData Formula data
 
-      * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
 
-      *
 
-      * @throws Exception
 
-      *
 
-      * @return array
 
-      */
 
-     private function getNextToken($formulaData, $baseCell = 'A1')
 
-     {
 
-         // offset: 0; size: 1; token id
 
-         $id = ord($formulaData[0]); // token id
 
-         $name = false; // initialize token name
 
-         switch ($id) {
 
-             case 0x03:
 
-                 $name = 'tAdd';
 
-                 $size = 1;
 
-                 $data = '+';
 
-                 break;
 
-             case 0x04:
 
-                 $name = 'tSub';
 
-                 $size = 1;
 
-                 $data = '-';
 
-                 break;
 
-             case 0x05:
 
-                 $name = 'tMul';
 
-                 $size = 1;
 
-                 $data = '*';
 
-                 break;
 
-             case 0x06:
 
-                 $name = 'tDiv';
 
-                 $size = 1;
 
-                 $data = '/';
 
-                 break;
 
-             case 0x07:
 
-                 $name = 'tPower';
 
-                 $size = 1;
 
-                 $data = '^';
 
-                 break;
 
-             case 0x08:
 
-                 $name = 'tConcat';
 
-                 $size = 1;
 
-                 $data = '&';
 
-                 break;
 
-             case 0x09:
 
-                 $name = 'tLT';
 
-                 $size = 1;
 
-                 $data = '<';
 
-                 break;
 
-             case 0x0A:
 
-                 $name = 'tLE';
 
-                 $size = 1;
 
-                 $data = '<=';
 
-                 break;
 
-             case 0x0B:
 
-                 $name = 'tEQ';
 
-                 $size = 1;
 
-                 $data = '=';
 
-                 break;
 
-             case 0x0C:
 
-                 $name = 'tGE';
 
-                 $size = 1;
 
-                 $data = '>=';
 
-                 break;
 
-             case 0x0D:
 
-                 $name = 'tGT';
 
-                 $size = 1;
 
-                 $data = '>';
 
-                 break;
 
-             case 0x0E:
 
-                 $name = 'tNE';
 
-                 $size = 1;
 
-                 $data = '<>';
 
-                 break;
 
-             case 0x0F:
 
-                 $name = 'tIsect';
 
-                 $size = 1;
 
-                 $data = ' ';
 
-                 break;
 
-             case 0x10:
 
-                 $name = 'tList';
 
-                 $size = 1;
 
-                 $data = ',';
 
-                 break;
 
-             case 0x11:
 
-                 $name = 'tRange';
 
-                 $size = 1;
 
-                 $data = ':';
 
-                 break;
 
-             case 0x12:
 
-                 $name = 'tUplus';
 
-                 $size = 1;
 
-                 $data = '+';
 
-                 break;
 
-             case 0x13:
 
-                 $name = 'tUminus';
 
-                 $size = 1;
 
-                 $data = '-';
 
-                 break;
 
-             case 0x14:
 
-                 $name = 'tPercent';
 
-                 $size = 1;
 
-                 $data = '%';
 
-                 break;
 
-             case 0x15:    //    parenthesis
 
-                 $name = 'tParen';
 
-                 $size = 1;
 
-                 $data = null;
 
-                 break;
 
-             case 0x16:    //    missing argument
 
-                 $name = 'tMissArg';
 
-                 $size = 1;
 
-                 $data = '';
 
-                 break;
 
-             case 0x17:    //    string
 
-                 $name = 'tStr';
 
-                 // offset: 1; size: var; Unicode string, 8-bit string length
 
-                 $string = self::readUnicodeStringShort(substr($formulaData, 1));
 
-                 $size = 1 + $string['size'];
 
-                 $data = self::UTF8toExcelDoubleQuoted($string['value']);
 
-                 break;
 
-             case 0x19:    //    Special attribute
 
-                 // offset: 1; size: 1; attribute type flags:
 
-                 switch (ord($formulaData[1])) {
 
-                     case 0x01:
 
-                         $name = 'tAttrVolatile';
 
-                         $size = 4;
 
-                         $data = null;
 
-                         break;
 
-                     case 0x02:
 
-                         $name = 'tAttrIf';
 
-                         $size = 4;
 
-                         $data = null;
 
-                         break;
 
-                     case 0x04:
 
-                         $name = 'tAttrChoose';
 
-                         // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1)
 
-                         $nc = self::getUInt2d($formulaData, 2);
 
-                         // offset: 4; size: 2 * $nc
 
-                         // offset: 4 + 2 * $nc; size: 2
 
-                         $size = 2 * $nc + 6;
 
-                         $data = null;
 
-                         break;
 
-                     case 0x08:
 
-                         $name = 'tAttrSkip';
 
-                         $size = 4;
 
-                         $data = null;
 
-                         break;
 
-                     case 0x10:
 
-                         $name = 'tAttrSum';
 
-                         $size = 4;
 
-                         $data = null;
 
-                         break;
 
-                     case 0x40:
 
-                     case 0x41:
 
-                         $name = 'tAttrSpace';
 
-                         $size = 4;
 
-                         // offset: 2; size: 2; space type and position
 
-                         switch (ord($formulaData[2])) {
 
-                             case 0x00:
 
-                                 $spacetype = 'type0';
 
-                                 break;
 
-                             case 0x01:
 
-                                 $spacetype = 'type1';
 
-                                 break;
 
-                             case 0x02:
 
-                                 $spacetype = 'type2';
 
-                                 break;
 
-                             case 0x03:
 
-                                 $spacetype = 'type3';
 
-                                 break;
 
-                             case 0x04:
 
-                                 $spacetype = 'type4';
 
-                                 break;
 
-                             case 0x05:
 
-                                 $spacetype = 'type5';
 
-                                 break;
 
-                             default:
 
-                                 throw new Exception('Unrecognized space type in tAttrSpace token');
 
-                                 break;
 
-                         }
 
-                         // offset: 3; size: 1; number of inserted spaces/carriage returns
 
-                         $spacecount = ord($formulaData[3]);
 
-                         $data = ['spacetype' => $spacetype, 'spacecount' => $spacecount];
 
-                         break;
 
-                     default:
 
-                         throw new Exception('Unrecognized attribute flag in tAttr token');
 
-                         break;
 
-                 }
 
-                 break;
 
-             case 0x1C:    //    error code
 
-                 // offset: 1; size: 1; error code
 
-                 $name = 'tErr';
 
-                 $size = 2;
 
-                 $data = Xls\ErrorCode::lookup(ord($formulaData[1]));
 
-                 break;
 
-             case 0x1D:    //    boolean
 
-                 // offset: 1; size: 1; 0 = false, 1 = true;
 
-                 $name = 'tBool';
 
-                 $size = 2;
 
-                 $data = ord($formulaData[1]) ? 'TRUE' : 'FALSE';
 
-                 break;
 
-             case 0x1E:    //    integer
 
-                 // offset: 1; size: 2; unsigned 16-bit integer
 
-                 $name = 'tInt';
 
-                 $size = 3;
 
-                 $data = self::getUInt2d($formulaData, 1);
 
-                 break;
 
-             case 0x1F:    //    number
 
-                 // offset: 1; size: 8;
 
-                 $name = 'tNum';
 
-                 $size = 9;
 
-                 $data = self::extractNumber(substr($formulaData, 1));
 
-                 $data = str_replace(',', '.', (string) $data); // in case non-English locale
 
-                 break;
 
-             case 0x20:    //    array constant
 
-             case 0x40:
 
-             case 0x60:
 
-                 // offset: 1; size: 7; not used
 
-                 $name = 'tArray';
 
-                 $size = 8;
 
-                 $data = null;
 
-                 break;
 
-             case 0x21:    //    function with fixed number of arguments
 
-             case 0x41:
 
-             case 0x61:
 
-                 $name = 'tFunc';
 
-                 $size = 3;
 
-                 // offset: 1; size: 2; index to built-in sheet function
 
-                 switch (self::getUInt2d($formulaData, 1)) {
 
-                     case 2:
 
-                         $function = 'ISNA';
 
-                         $args = 1;
 
-                         break;
 
-                     case 3:
 
-                         $function = 'ISERROR';
 
-                         $args = 1;
 
-                         break;
 
-                     case 10:
 
-                         $function = 'NA';
 
-                         $args = 0;
 
-                         break;
 
-                     case 15:
 
-                         $function = 'SIN';
 
-                         $args = 1;
 
-                         break;
 
-                     case 16:
 
-                         $function = 'COS';
 
-                         $args = 1;
 
-                         break;
 
-                     case 17:
 
-                         $function = 'TAN';
 
-                         $args = 1;
 
-                         break;
 
-                     case 18:
 
-                         $function = 'ATAN';
 
-                         $args = 1;
 
-                         break;
 
-                     case 19:
 
-                         $function = 'PI';
 
-                         $args = 0;
 
-                         break;
 
-                     case 20:
 
-                         $function = 'SQRT';
 
-                         $args = 1;
 
-                         break;
 
-                     case 21:
 
-                         $function = 'EXP';
 
-                         $args = 1;
 
-                         break;
 
-                     case 22:
 
-                         $function = 'LN';
 
-                         $args = 1;
 
-                         break;
 
-                     case 23:
 
-                         $function = 'LOG10';
 
-                         $args = 1;
 
-                         break;
 
-                     case 24:
 
-                         $function = 'ABS';
 
-                         $args = 1;
 
-                         break;
 
-                     case 25:
 
-                         $function = 'INT';
 
-                         $args = 1;
 
-                         break;
 
-                     case 26:
 
-                         $function = 'SIGN';
 
-                         $args = 1;
 
-                         break;
 
-                     case 27:
 
-                         $function = 'ROUND';
 
-                         $args = 2;
 
-                         break;
 
-                     case 30:
 
-                         $function = 'REPT';
 
-                         $args = 2;
 
-                         break;
 
-                     case 31:
 
-                         $function = 'MID';
 
-                         $args = 3;
 
-                         break;
 
-                     case 32:
 
-                         $function = 'LEN';
 
-                         $args = 1;
 
-                         break;
 
-                     case 33:
 
-                         $function = 'VALUE';
 
-                         $args = 1;
 
-                         break;
 
-                     case 34:
 
-                         $function = 'TRUE';
 
-                         $args = 0;
 
-                         break;
 
-                     case 35:
 
-                         $function = 'FALSE';
 
-                         $args = 0;
 
-                         break;
 
-                     case 38:
 
-                         $function = 'NOT';
 
-                         $args = 1;
 
-                         break;
 
-                     case 39:
 
-                         $function = 'MOD';
 
-                         $args = 2;
 
-                         break;
 
-                     case 40:
 
-                         $function = 'DCOUNT';
 
-                         $args = 3;
 
-                         break;
 
-                     case 41:
 
-                         $function = 'DSUM';
 
-                         $args = 3;
 
-                         break;
 
-                     case 42:
 
-                         $function = 'DAVERAGE';
 
-                         $args = 3;
 
-                         break;
 
-                     case 43:
 
-                         $function = 'DMIN';
 
-                         $args = 3;
 
-                         break;
 
-                     case 44:
 
-                         $function = 'DMAX';
 
-                         $args = 3;
 
-                         break;
 
-                     case 45:
 
-                         $function = 'DSTDEV';
 
-                         $args = 3;
 
-                         break;
 
-                     case 48:
 
-                         $function = 'TEXT';
 
-                         $args = 2;
 
-                         break;
 
-                     case 61:
 
-                         $function = 'MIRR';
 
-                         $args = 3;
 
-                         break;
 
-                     case 63:
 
-                         $function = 'RAND';
 
-                         $args = 0;
 
-                         break;
 
-                     case 65:
 
-                         $function = 'DATE';
 
-                         $args = 3;
 
-                         break;
 
-                     case 66:
 
-                         $function = 'TIME';
 
-                         $args = 3;
 
-                         break;
 
-                     case 67:
 
-                         $function = 'DAY';
 
-                         $args = 1;
 
-                         break;
 
-                     case 68:
 
-                         $function = 'MONTH';
 
-                         $args = 1;
 
-                         break;
 
-                     case 69:
 
-                         $function = 'YEAR';
 
-                         $args = 1;
 
-                         break;
 
-                     case 71:
 
-                         $function = 'HOUR';
 
-                         $args = 1;
 
-                         break;
 
-                     case 72:
 
-                         $function = 'MINUTE';
 
-                         $args = 1;
 
-                         break;
 
-                     case 73:
 
-                         $function = 'SECOND';
 
-                         $args = 1;
 
-                         break;
 
-                     case 74:
 
-                         $function = 'NOW';
 
-                         $args = 0;
 
-                         break;
 
-                     case 75:
 
-                         $function = 'AREAS';
 
-                         $args = 1;
 
-                         break;
 
-                     case 76:
 
-                         $function = 'ROWS';
 
-                         $args = 1;
 
-                         break;
 
-                     case 77:
 
-                         $function = 'COLUMNS';
 
-                         $args = 1;
 
-                         break;
 
-                     case 83:
 
-                         $function = 'TRANSPOSE';
 
-                         $args = 1;
 
-                         break;
 
-                     case 86:
 
-                         $function = 'TYPE';
 
-                         $args = 1;
 
-                         break;
 
-                     case 97:
 
-                         $function = 'ATAN2';
 
-                         $args = 2;
 
-                         break;
 
-                     case 98:
 
-                         $function = 'ASIN';
 
-                         $args = 1;
 
-                         break;
 
-                     case 99:
 
-                         $function = 'ACOS';
 
-                         $args = 1;
 
-                         break;
 
-                     case 105:
 
-                         $function = 'ISREF';
 
-                         $args = 1;
 
-                         break;
 
-                     case 111:
 
-                         $function = 'CHAR';
 
-                         $args = 1;
 
-                         break;
 
-                     case 112:
 
-                         $function = 'LOWER';
 
-                         $args = 1;
 
-                         break;
 
-                     case 113:
 
-                         $function = 'UPPER';
 
-                         $args = 1;
 
-                         break;
 
-                     case 114:
 
-                         $function = 'PROPER';
 
-                         $args = 1;
 
-                         break;
 
-                     case 117:
 
-                         $function = 'EXACT';
 
-                         $args = 2;
 
-                         break;
 
-                     case 118:
 
-                         $function = 'TRIM';
 
-                         $args = 1;
 
-                         break;
 
-                     case 119:
 
-                         $function = 'REPLACE';
 
-                         $args = 4;
 
-                         break;
 
-                     case 121:
 
-                         $function = 'CODE';
 
-                         $args = 1;
 
-                         break;
 
-                     case 126:
 
-                         $function = 'ISERR';
 
-                         $args = 1;
 
-                         break;
 
-                     case 127:
 
-                         $function = 'ISTEXT';
 
-                         $args = 1;
 
-                         break;
 
-                     case 128:
 
-                         $function = 'ISNUMBER';
 
-                         $args = 1;
 
-                         break;
 
-                     case 129:
 
-                         $function = 'ISBLANK';
 
-                         $args = 1;
 
-                         break;
 
-                     case 130:
 
-                         $function = 'T';
 
-                         $args = 1;
 
-                         break;
 
-                     case 131:
 
-                         $function = 'N';
 
-                         $args = 1;
 
-                         break;
 
-                     case 140:
 
-                         $function = 'DATEVALUE';
 
-                         $args = 1;
 
-                         break;
 
-                     case 141:
 
-                         $function = 'TIMEVALUE';
 
-                         $args = 1;
 
-                         break;
 
-                     case 142:
 
-                         $function = 'SLN';
 
-                         $args = 3;
 
-                         break;
 
-                     case 143:
 
-                         $function = 'SYD';
 
-                         $args = 4;
 
-                         break;
 
-                     case 162:
 
-                         $function = 'CLEAN';
 
-                         $args = 1;
 
-                         break;
 
-                     case 163:
 
-                         $function = 'MDETERM';
 
-                         $args = 1;
 
-                         break;
 
-                     case 164:
 
-                         $function = 'MINVERSE';
 
-                         $args = 1;
 
-                         break;
 
-                     case 165:
 
-                         $function = 'MMULT';
 
-                         $args = 2;
 
-                         break;
 
-                     case 184:
 
-                         $function = 'FACT';
 
-                         $args = 1;
 
-                         break;
 
-                     case 189:
 
-                         $function = 'DPRODUCT';
 
-                         $args = 3;
 
-                         break;
 
-                     case 190:
 
-                         $function = 'ISNONTEXT';
 
-                         $args = 1;
 
-                         break;
 
-                     case 195:
 
-                         $function = 'DSTDEVP';
 
-                         $args = 3;
 
-                         break;
 
-                     case 196:
 
-                         $function = 'DVARP';
 
-                         $args = 3;
 
-                         break;
 
-                     case 198:
 
-                         $function = 'ISLOGICAL';
 
-                         $args = 1;
 
-                         break;
 
-                     case 199:
 
-                         $function = 'DCOUNTA';
 
-                         $args = 3;
 
-                         break;
 
-                     case 207:
 
-                         $function = 'REPLACEB';
 
-                         $args = 4;
 
-                         break;
 
-                     case 210:
 
-                         $function = 'MIDB';
 
-                         $args = 3;
 
-                         break;
 
-                     case 211:
 
-                         $function = 'LENB';
 
-                         $args = 1;
 
-                         break;
 
-                     case 212:
 
-                         $function = 'ROUNDUP';
 
-                         $args = 2;
 
-                         break;
 
-                     case 213:
 
-                         $function = 'ROUNDDOWN';
 
-                         $args = 2;
 
-                         break;
 
-                     case 214:
 
-                         $function = 'ASC';
 
-                         $args = 1;
 
-                         break;
 
-                     case 215:
 
-                         $function = 'DBCS';
 
-                         $args = 1;
 
-                         break;
 
-                     case 221:
 
-                         $function = 'TODAY';
 
-                         $args = 0;
 
-                         break;
 
-                     case 229:
 
-                         $function = 'SINH';
 
-                         $args = 1;
 
-                         break;
 
-                     case 230:
 
-                         $function = 'COSH';
 
-                         $args = 1;
 
-                         break;
 
-                     case 231:
 
-                         $function = 'TANH';
 
-                         $args = 1;
 
-                         break;
 
-                     case 232:
 
-                         $function = 'ASINH';
 
-                         $args = 1;
 
-                         break;
 
-                     case 233:
 
-                         $function = 'ACOSH';
 
-                         $args = 1;
 
-                         break;
 
-                     case 234:
 
-                         $function = 'ATANH';
 
-                         $args = 1;
 
-                         break;
 
-                     case 235:
 
-                         $function = 'DGET';
 
-                         $args = 3;
 
-                         break;
 
-                     case 244:
 
-                         $function = 'INFO';
 
-                         $args = 1;
 
-                         break;
 
-                     case 252:
 
-                         $function = 'FREQUENCY';
 
-                         $args = 2;
 
-                         break;
 
-                     case 261:
 
-                         $function = 'ERROR.TYPE';
 
-                         $args = 1;
 
-                         break;
 
-                     case 271:
 
-                         $function = 'GAMMALN';
 
-                         $args = 1;
 
-                         break;
 
-                     case 273:
 
-                         $function = 'BINOMDIST';
 
-                         $args = 4;
 
-                         break;
 
-                     case 274:
 
-                         $function = 'CHIDIST';
 
-                         $args = 2;
 
-                         break;
 
-                     case 275:
 
-                         $function = 'CHIINV';
 
-                         $args = 2;
 
-                         break;
 
-                     case 276:
 
-                         $function = 'COMBIN';
 
-                         $args = 2;
 
-                         break;
 
-                     case 277:
 
-                         $function = 'CONFIDENCE';
 
-                         $args = 3;
 
-                         break;
 
-                     case 278:
 
-                         $function = 'CRITBINOM';
 
-                         $args = 3;
 
-                         break;
 
-                     case 279:
 
-                         $function = 'EVEN';
 
-                         $args = 1;
 
-                         break;
 
-                     case 280:
 
-                         $function = 'EXPONDIST';
 
-                         $args = 3;
 
-                         break;
 
-                     case 281:
 
-                         $function = 'FDIST';
 
-                         $args = 3;
 
-                         break;
 
-                     case 282:
 
-                         $function = 'FINV';
 
-                         $args = 3;
 
-                         break;
 
-                     case 283:
 
-                         $function = 'FISHER';
 
-                         $args = 1;
 
-                         break;
 
-                     case 284:
 
-                         $function = 'FISHERINV';
 
-                         $args = 1;
 
-                         break;
 
-                     case 285:
 
-                         $function = 'FLOOR';
 
-                         $args = 2;
 
-                         break;
 
-                     case 286:
 
-                         $function = 'GAMMADIST';
 
-                         $args = 4;
 
-                         break;
 
-                     case 287:
 
-                         $function = 'GAMMAINV';
 
-                         $args = 3;
 
-                         break;
 
-                     case 288:
 
-                         $function = 'CEILING';
 
-                         $args = 2;
 
-                         break;
 
-                     case 289:
 
-                         $function = 'HYPGEOMDIST';
 
-                         $args = 4;
 
-                         break;
 
-                     case 290:
 
-                         $function = 'LOGNORMDIST';
 
-                         $args = 3;
 
-                         break;
 
-                     case 291:
 
-                         $function = 'LOGINV';
 
-                         $args = 3;
 
-                         break;
 
-                     case 292:
 
-                         $function = 'NEGBINOMDIST';
 
-                         $args = 3;
 
-                         break;
 
-                     case 293:
 
-                         $function = 'NORMDIST';
 
-                         $args = 4;
 
-                         break;
 
-                     case 294:
 
-                         $function = 'NORMSDIST';
 
-                         $args = 1;
 
-                         break;
 
-                     case 295:
 
-                         $function = 'NORMINV';
 
-                         $args = 3;
 
-                         break;
 
-                     case 296:
 
-                         $function = 'NORMSINV';
 
-                         $args = 1;
 
-                         break;
 
-                     case 297:
 
-                         $function = 'STANDARDIZE';
 
-                         $args = 3;
 
-                         break;
 
-                     case 298:
 
-                         $function = 'ODD';
 
-                         $args = 1;
 
-                         break;
 
-                     case 299:
 
-                         $function = 'PERMUT';
 
-                         $args = 2;
 
-                         break;
 
-                     case 300:
 
-                         $function = 'POISSON';
 
-                         $args = 3;
 
-                         break;
 
-                     case 301:
 
-                         $function = 'TDIST';
 
-                         $args = 3;
 
-                         break;
 
-                     case 302:
 
-                         $function = 'WEIBULL';
 
-                         $args = 4;
 
-                         break;
 
-                     case 303:
 
-                         $function = 'SUMXMY2';
 
-                         $args = 2;
 
-                         break;
 
-                     case 304:
 
-                         $function = 'SUMX2MY2';
 
-                         $args = 2;
 
-                         break;
 
-                     case 305:
 
-                         $function = 'SUMX2PY2';
 
-                         $args = 2;
 
-                         break;
 
-                     case 306:
 
-                         $function = 'CHITEST';
 
-                         $args = 2;
 
-                         break;
 
-                     case 307:
 
-                         $function = 'CORREL';
 
-                         $args = 2;
 
-                         break;
 
-                     case 308:
 
-                         $function = 'COVAR';
 
-                         $args = 2;
 
-                         break;
 
-                     case 309:
 
-                         $function = 'FORECAST';
 
-                         $args = 3;
 
-                         break;
 
-                     case 310:
 
-                         $function = 'FTEST';
 
-                         $args = 2;
 
-                         break;
 
-                     case 311:
 
-                         $function = 'INTERCEPT';
 
-                         $args = 2;
 
-                         break;
 
-                     case 312:
 
-                         $function = 'PEARSON';
 
-                         $args = 2;
 
-                         break;
 
-                     case 313:
 
-                         $function = 'RSQ';
 
-                         $args = 2;
 
-                         break;
 
-                     case 314:
 
-                         $function = 'STEYX';
 
-                         $args = 2;
 
-                         break;
 
-                     case 315:
 
-                         $function = 'SLOPE';
 
-                         $args = 2;
 
-                         break;
 
-                     case 316:
 
-                         $function = 'TTEST';
 
-                         $args = 4;
 
-                         break;
 
-                     case 325:
 
-                         $function = 'LARGE';
 
-                         $args = 2;
 
-                         break;
 
-                     case 326:
 
-                         $function = 'SMALL';
 
-                         $args = 2;
 
-                         break;
 
-                     case 327:
 
-                         $function = 'QUARTILE';
 
-                         $args = 2;
 
-                         break;
 
-                     case 328:
 
-                         $function = 'PERCENTILE';
 
-                         $args = 2;
 
-                         break;
 
-                     case 331:
 
-                         $function = 'TRIMMEAN';
 
-                         $args = 2;
 
-                         break;
 
-                     case 332:
 
-                         $function = 'TINV';
 
-                         $args = 2;
 
-                         break;
 
-                     case 337:
 
-                         $function = 'POWER';
 
-                         $args = 2;
 
-                         break;
 
-                     case 342:
 
-                         $function = 'RADIANS';
 
-                         $args = 1;
 
-                         break;
 
-                     case 343:
 
-                         $function = 'DEGREES';
 
-                         $args = 1;
 
-                         break;
 
-                     case 346:
 
-                         $function = 'COUNTIF';
 
-                         $args = 2;
 
-                         break;
 
-                     case 347:
 
-                         $function = 'COUNTBLANK';
 
-                         $args = 1;
 
-                         break;
 
-                     case 350:
 
-                         $function = 'ISPMT';
 
-                         $args = 4;
 
-                         break;
 
-                     case 351:
 
-                         $function = 'DATEDIF';
 
-                         $args = 3;
 
-                         break;
 
-                     case 352:
 
-                         $function = 'DATESTRING';
 
-                         $args = 1;
 
-                         break;
 
-                     case 353:
 
-                         $function = 'NUMBERSTRING';
 
-                         $args = 2;
 
-                         break;
 
-                     case 360:
 
-                         $function = 'PHONETIC';
 
-                         $args = 1;
 
-                         break;
 
-                     case 368:
 
-                         $function = 'BAHTTEXT';
 
-                         $args = 1;
 
-                         break;
 
-                     default:
 
-                         throw new Exception('Unrecognized function in formula');
 
-                         break;
 
-                 }
 
-                 $data = ['function' => $function, 'args' => $args];
 
-                 break;
 
-             case 0x22:    //    function with variable number of arguments
 
-             case 0x42:
 
-             case 0x62:
 
-                 $name = 'tFuncV';
 
-                 $size = 4;
 
-                 // offset: 1; size: 1; number of arguments
 
-                 $args = ord($formulaData[1]);
 
-                 // offset: 2: size: 2; index to built-in sheet function
 
-                 $index = self::getUInt2d($formulaData, 2);
 
-                 switch ($index) {
 
-                     case 0:
 
-                         $function = 'COUNT';
 
-                         break;
 
-                     case 1:
 
-                         $function = 'IF';
 
-                         break;
 
-                     case 4:
 
-                         $function = 'SUM';
 
-                         break;
 
-                     case 5:
 
-                         $function = 'AVERAGE';
 
-                         break;
 
-                     case 6:
 
-                         $function = 'MIN';
 
-                         break;
 
-                     case 7:
 
-                         $function = 'MAX';
 
-                         break;
 
-                     case 8:
 
-                         $function = 'ROW';
 
-                         break;
 
-                     case 9:
 
-                         $function = 'COLUMN';
 
-                         break;
 
-                     case 11:
 
-                         $function = 'NPV';
 
-                         break;
 
-                     case 12:
 
-                         $function = 'STDEV';
 
-                         break;
 
-                     case 13:
 
-                         $function = 'DOLLAR';
 
-                         break;
 
-                     case 14:
 
-                         $function = 'FIXED';
 
-                         break;
 
-                     case 28:
 
-                         $function = 'LOOKUP';
 
-                         break;
 
-                     case 29:
 
-                         $function = 'INDEX';
 
-                         break;
 
-                     case 36:
 
-                         $function = 'AND';
 
-                         break;
 
-                     case 37:
 
-                         $function = 'OR';
 
-                         break;
 
-                     case 46:
 
-                         $function = 'VAR';
 
-                         break;
 
-                     case 49:
 
-                         $function = 'LINEST';
 
-                         break;
 
-                     case 50:
 
-                         $function = 'TREND';
 
-                         break;
 
-                     case 51:
 
-                         $function = 'LOGEST';
 
-                         break;
 
-                     case 52:
 
-                         $function = 'GROWTH';
 
-                         break;
 
-                     case 56:
 
-                         $function = 'PV';
 
-                         break;
 
-                     case 57:
 
-                         $function = 'FV';
 
-                         break;
 
-                     case 58:
 
-                         $function = 'NPER';
 
-                         break;
 
-                     case 59:
 
-                         $function = 'PMT';
 
-                         break;
 
-                     case 60:
 
-                         $function = 'RATE';
 
-                         break;
 
-                     case 62:
 
-                         $function = 'IRR';
 
-                         break;
 
-                     case 64:
 
-                         $function = 'MATCH';
 
-                         break;
 
-                     case 70:
 
-                         $function = 'WEEKDAY';
 
-                         break;
 
-                     case 78:
 
-                         $function = 'OFFSET';
 
-                         break;
 
-                     case 82:
 
-                         $function = 'SEARCH';
 
-                         break;
 
-                     case 100:
 
-                         $function = 'CHOOSE';
 
-                         break;
 
-                     case 101:
 
-                         $function = 'HLOOKUP';
 
-                         break;
 
-                     case 102:
 
-                         $function = 'VLOOKUP';
 
-                         break;
 
-                     case 109:
 
-                         $function = 'LOG';
 
-                         break;
 
-                     case 115:
 
-                         $function = 'LEFT';
 
-                         break;
 
-                     case 116:
 
-                         $function = 'RIGHT';
 
-                         break;
 
-                     case 120:
 
-                         $function = 'SUBSTITUTE';
 
-                         break;
 
-                     case 124:
 
-                         $function = 'FIND';
 
-                         break;
 
-                     case 125:
 
-                         $function = 'CELL';
 
-                         break;
 
-                     case 144:
 
-                         $function = 'DDB';
 
-                         break;
 
-                     case 148:
 
-                         $function = 'INDIRECT';
 
-                         break;
 
-                     case 167:
 
-                         $function = 'IPMT';
 
-                         break;
 
-                     case 168:
 
-                         $function = 'PPMT';
 
-                         break;
 
-                     case 169:
 
-                         $function = 'COUNTA';
 
-                         break;
 
-                     case 183:
 
-                         $function = 'PRODUCT';
 
-                         break;
 
-                     case 193:
 
-                         $function = 'STDEVP';
 
-                         break;
 
-                     case 194:
 
-                         $function = 'VARP';
 
-                         break;
 
-                     case 197:
 
-                         $function = 'TRUNC';
 
-                         break;
 
-                     case 204:
 
-                         $function = 'USDOLLAR';
 
-                         break;
 
-                     case 205:
 
-                         $function = 'FINDB';
 
-                         break;
 
-                     case 206:
 
-                         $function = 'SEARCHB';
 
-                         break;
 
-                     case 208:
 
-                         $function = 'LEFTB';
 
-                         break;
 
-                     case 209:
 
-                         $function = 'RIGHTB';
 
-                         break;
 
-                     case 216:
 
-                         $function = 'RANK';
 
-                         break;
 
-                     case 219:
 
-                         $function = 'ADDRESS';
 
-                         break;
 
-                     case 220:
 
-                         $function = 'DAYS360';
 
-                         break;
 
-                     case 222:
 
-                         $function = 'VDB';
 
-                         break;
 
-                     case 227:
 
-                         $function = 'MEDIAN';
 
-                         break;
 
-                     case 228:
 
-                         $function = 'SUMPRODUCT';
 
-                         break;
 
-                     case 247:
 
-                         $function = 'DB';
 
-                         break;
 
-                     case 255:
 
-                         $function = '';
 
-                         break;
 
-                     case 269:
 
-                         $function = 'AVEDEV';
 
-                         break;
 
-                     case 270:
 
-                         $function = 'BETADIST';
 
-                         break;
 
-                     case 272:
 
-                         $function = 'BETAINV';
 
-                         break;
 
-                     case 317:
 
-                         $function = 'PROB';
 
-                         break;
 
-                     case 318:
 
-                         $function = 'DEVSQ';
 
-                         break;
 
-                     case 319:
 
-                         $function = 'GEOMEAN';
 
-                         break;
 
-                     case 320:
 
-                         $function = 'HARMEAN';
 
-                         break;
 
-                     case 321:
 
-                         $function = 'SUMSQ';
 
-                         break;
 
-                     case 322:
 
-                         $function = 'KURT';
 
-                         break;
 
-                     case 323:
 
-                         $function = 'SKEW';
 
-                         break;
 
-                     case 324:
 
-                         $function = 'ZTEST';
 
-                         break;
 
-                     case 329:
 
-                         $function = 'PERCENTRANK';
 
-                         break;
 
-                     case 330:
 
-                         $function = 'MODE';
 
-                         break;
 
-                     case 336:
 
-                         $function = 'CONCATENATE';
 
-                         break;
 
-                     case 344:
 
-                         $function = 'SUBTOTAL';
 
-                         break;
 
-                     case 345:
 
-                         $function = 'SUMIF';
 
-                         break;
 
-                     case 354:
 
-                         $function = 'ROMAN';
 
-                         break;
 
-                     case 358:
 
-                         $function = 'GETPIVOTDATA';
 
-                         break;
 
-                     case 359:
 
-                         $function = 'HYPERLINK';
 
-                         break;
 
-                     case 361:
 
-                         $function = 'AVERAGEA';
 
-                         break;
 
-                     case 362:
 
-                         $function = 'MAXA';
 
-                         break;
 
-                     case 363:
 
-                         $function = 'MINA';
 
-                         break;
 
-                     case 364:
 
-                         $function = 'STDEVPA';
 
-                         break;
 
-                     case 365:
 
-                         $function = 'VARPA';
 
-                         break;
 
-                     case 366:
 
-                         $function = 'STDEVA';
 
-                         break;
 
-                     case 367:
 
-                         $function = 'VARA';
 
-                         break;
 
-                     default:
 
-                         throw new Exception('Unrecognized function in formula');
 
-                         break;
 
-                 }
 
-                 $data = ['function' => $function, 'args' => $args];
 
-                 break;
 
-             case 0x23:    //    index to defined name
 
-             case 0x43:
 
-             case 0x63:
 
-                 $name = 'tName';
 
-                 $size = 5;
 
-                 // offset: 1; size: 2; one-based index to definedname record
 
-                 $definedNameIndex = self::getUInt2d($formulaData, 1) - 1;
 
-                 // offset: 2; size: 2; not used
 
-                 $data = $this->definedname[$definedNameIndex]['name'];
 
-                 break;
 
-             case 0x24:    //    single cell reference e.g. A5
 
-             case 0x44:
 
-             case 0x64:
 
-                 $name = 'tRef';
 
-                 $size = 5;
 
-                 $data = $this->readBIFF8CellAddress(substr($formulaData, 1, 4));
 
-                 break;
 
-             case 0x25:    //    cell range reference to cells in the same sheet (2d)
 
-             case 0x45:
 
-             case 0x65:
 
-                 $name = 'tArea';
 
-                 $size = 9;
 
-                 $data = $this->readBIFF8CellRangeAddress(substr($formulaData, 1, 8));
 
-                 break;
 
-             case 0x26:    //    Constant reference sub-expression
 
-             case 0x46:
 
-             case 0x66:
 
-                 $name = 'tMemArea';
 
-                 // offset: 1; size: 4; not used
 
-                 // offset: 5; size: 2; size of the following subexpression
 
-                 $subSize = self::getUInt2d($formulaData, 5);
 
-                 $size = 7 + $subSize;
 
-                 $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
 
-                 break;
 
-             case 0x27:    //    Deleted constant reference sub-expression
 
-             case 0x47:
 
-             case 0x67:
 
-                 $name = 'tMemErr';
 
-                 // offset: 1; size: 4; not used
 
-                 // offset: 5; size: 2; size of the following subexpression
 
-                 $subSize = self::getUInt2d($formulaData, 5);
 
-                 $size = 7 + $subSize;
 
-                 $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
 
-                 break;
 
-             case 0x29:    //    Variable reference sub-expression
 
-             case 0x49:
 
-             case 0x69:
 
-                 $name = 'tMemFunc';
 
-                 // offset: 1; size: 2; size of the following sub-expression
 
-                 $subSize = self::getUInt2d($formulaData, 1);
 
-                 $size = 3 + $subSize;
 
-                 $data = $this->getFormulaFromData(substr($formulaData, 3, $subSize));
 
-                 break;
 
-             case 0x2C: // Relative 2d cell reference reference, used in shared formulas and some other places
 
-             case 0x4C:
 
-             case 0x6C:
 
-                 $name = 'tRefN';
 
-                 $size = 5;
 
-                 $data = $this->readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell);
 
-                 break;
 
-             case 0x2D:    //    Relative 2d range reference
 
-             case 0x4D:
 
-             case 0x6D:
 
-                 $name = 'tAreaN';
 
-                 $size = 9;
 
-                 $data = $this->readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell);
 
-                 break;
 
-             case 0x39:    //    External name
 
-             case 0x59:
 
-             case 0x79:
 
-                 $name = 'tNameX';
 
-                 $size = 7;
 
-                 // offset: 1; size: 2; index to REF entry in EXTERNSHEET record
 
-                 // offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record
 
-                 $index = self::getUInt2d($formulaData, 3);
 
-                 // assume index is to EXTERNNAME record
 
-                 $data = $this->externalNames[$index - 1]['name'];
 
-                 // offset: 5; size: 2; not used
 
-                 break;
 
-             case 0x3A:    //    3d reference to cell
 
-             case 0x5A:
 
-             case 0x7A:
 
-                 $name = 'tRef3d';
 
-                 $size = 7;
 
-                 try {
 
-                     // offset: 1; size: 2; index to REF entry
 
-                     $sheetRange = $this->readSheetRangeByRefIndex(self::getUInt2d($formulaData, 1));
 
-                     // offset: 3; size: 4; cell address
 
-                     $cellAddress = $this->readBIFF8CellAddress(substr($formulaData, 3, 4));
 
-                     $data = "$sheetRange!$cellAddress";
 
-                 } catch (PhpSpreadsheetException $e) {
 
-                     // deleted sheet reference
 
-                     $data = '#REF!';
 
-                 }
 
-                 break;
 
-             case 0x3B:    //    3d reference to cell range
 
-             case 0x5B:
 
-             case 0x7B:
 
-                 $name = 'tArea3d';
 
-                 $size = 11;
 
-                 try {
 
-                     // offset: 1; size: 2; index to REF entry
 
-                     $sheetRange = $this->readSheetRangeByRefIndex(self::getUInt2d($formulaData, 1));
 
-                     // offset: 3; size: 8; cell address
 
-                     $cellRangeAddress = $this->readBIFF8CellRangeAddress(substr($formulaData, 3, 8));
 
-                     $data = "$sheetRange!$cellRangeAddress";
 
-                 } catch (PhpSpreadsheetException $e) {
 
-                     // deleted sheet reference
 
-                     $data = '#REF!';
 
-                 }
 
-                 break;
 
-             // Unknown cases    // don't know how to deal with
 
-             default:
 
-                 throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula');
 
-                 break;
 
-         }
 
-         return [
 
-             'id' => $id,
 
-             'name' => $name,
 
-             'size' => $size,
 
-             'data' => $data,
 
-         ];
 
-     }
 
-     /**
 
-      * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2'
 
-      * section 3.3.4.
 
-      *
 
-      * @param string $cellAddressStructure
 
-      *
 
-      * @return string
 
-      */
 
-     private function readBIFF8CellAddress($cellAddressStructure)
 
-     {
 
-         // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
 
-         $row = self::getUInt2d($cellAddressStructure, 0) + 1;
 
-         // offset: 2; size: 2; index to column or column offset + relative flags
 
-         // bit: 7-0; mask 0x00FF; column index
 
-         $column = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($cellAddressStructure, 2)) + 1);
 
-         // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
 
-         if (!(0x4000 & self::getUInt2d($cellAddressStructure, 2))) {
 
-             $column = '$' . $column;
 
-         }
 
-         // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
 
-         if (!(0x8000 & self::getUInt2d($cellAddressStructure, 2))) {
 
-             $row = '$' . $row;
 
-         }
 
-         return $column . $row;
 
-     }
 
-     /**
 
-      * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column
 
-      * to indicate offsets from a base cell
 
-      * section 3.3.4.
 
-      *
 
-      * @param string $cellAddressStructure
 
-      * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
 
-      *
 
-      * @return string
 
-      */
 
-     private function readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1')
 
-     {
 
-         list($baseCol, $baseRow) = Coordinate::coordinateFromString($baseCell);
 
-         $baseCol = Coordinate::columnIndexFromString($baseCol) - 1;
 
-         // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
 
-         $rowIndex = self::getUInt2d($cellAddressStructure, 0);
 
-         $row = self::getUInt2d($cellAddressStructure, 0) + 1;
 
-         // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
 
-         if (!(0x4000 & self::getUInt2d($cellAddressStructure, 2))) {
 
-             // offset: 2; size: 2; index to column or column offset + relative flags
 
-             // bit: 7-0; mask 0x00FF; column index
 
-             $colIndex = 0x00FF & self::getUInt2d($cellAddressStructure, 2);
 
-             $column = Coordinate::stringFromColumnIndex($colIndex + 1);
 
-             $column = '$' . $column;
 
-         } else {
 
-             // offset: 2; size: 2; index to column or column offset + relative flags
 
-             // bit: 7-0; mask 0x00FF; column index
 
-             $relativeColIndex = 0x00FF & self::getInt2d($cellAddressStructure, 2);
 
-             $colIndex = $baseCol + $relativeColIndex;
 
-             $colIndex = ($colIndex < 256) ? $colIndex : $colIndex - 256;
 
-             $colIndex = ($colIndex >= 0) ? $colIndex : $colIndex + 256;
 
-             $column = Coordinate::stringFromColumnIndex($colIndex + 1);
 
-         }
 
-         // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
 
-         if (!(0x8000 & self::getUInt2d($cellAddressStructure, 2))) {
 
-             $row = '$' . $row;
 
-         } else {
 
-             $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536;
 
-             $row = $baseRow + $rowIndex;
 
-         }
 
-         return $column . $row;
 
-     }
 
-     /**
 
-      * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1'
 
-      * always fixed range
 
-      * section 2.5.14.
 
-      *
 
-      * @param string $subData
 
-      *
 
-      * @throws Exception
 
-      *
 
-      * @return string
 
-      */
 
-     private function readBIFF5CellRangeAddressFixed($subData)
 
-     {
 
-         // offset: 0; size: 2; index to first row
 
-         $fr = self::getUInt2d($subData, 0) + 1;
 
-         // offset: 2; size: 2; index to last row
 
-         $lr = self::getUInt2d($subData, 2) + 1;
 
-         // offset: 4; size: 1; index to first column
 
-         $fc = ord($subData[4]);
 
-         // offset: 5; size: 1; index to last column
 
-         $lc = ord($subData[5]);
 
-         // check values
 
-         if ($fr > $lr || $fc > $lc) {
 
-             throw new Exception('Not a cell range address');
 
-         }
 
-         // column index to letter
 
-         $fc = Coordinate::stringFromColumnIndex($fc + 1);
 
-         $lc = Coordinate::stringFromColumnIndex($lc + 1);
 
-         if ($fr == $lr and $fc == $lc) {
 
-             return "$fc$fr";
 
-         }
 
-         return "$fc$fr:$lc$lr";
 
-     }
 
-     /**
 
-      * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1'
 
-      * always fixed range
 
-      * section 2.5.14.
 
-      *
 
-      * @param string $subData
 
-      *
 
-      * @throws Exception
 
-      *
 
-      * @return string
 
-      */
 
-     private function readBIFF8CellRangeAddressFixed($subData)
 
-     {
 
-         // offset: 0; size: 2; index to first row
 
-         $fr = self::getUInt2d($subData, 0) + 1;
 
-         // offset: 2; size: 2; index to last row
 
-         $lr = self::getUInt2d($subData, 2) + 1;
 
-         // offset: 4; size: 2; index to first column
 
-         $fc = self::getUInt2d($subData, 4);
 
-         // offset: 6; size: 2; index to last column
 
-         $lc = self::getUInt2d($subData, 6);
 
-         // check values
 
-         if ($fr > $lr || $fc > $lc) {
 
-             throw new Exception('Not a cell range address');
 
-         }
 
-         // column index to letter
 
-         $fc = Coordinate::stringFromColumnIndex($fc + 1);
 
-         $lc = Coordinate::stringFromColumnIndex($lc + 1);
 
-         if ($fr == $lr and $fc == $lc) {
 
-             return "$fc$fr";
 
-         }
 
-         return "$fc$fr:$lc$lr";
 
-     }
 
-     /**
 
-      * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6'
 
-      * there are flags indicating whether column/row index is relative
 
-      * section 3.3.4.
 
-      *
 
-      * @param string $subData
 
-      *
 
-      * @return string
 
-      */
 
-     private function readBIFF8CellRangeAddress($subData)
 
-     {
 
-         // todo: if cell range is just a single cell, should this funciton
 
-         // not just return e.g. 'A1' and not 'A1:A1' ?
 
-         // offset: 0; size: 2; index to first row (0... 65535) (or offset (-32768... 32767))
 
-         $fr = self::getUInt2d($subData, 0) + 1;
 
-         // offset: 2; size: 2; index to last row (0... 65535) (or offset (-32768... 32767))
 
-         $lr = self::getUInt2d($subData, 2) + 1;
 
-         // offset: 4; size: 2; index to first column or column offset + relative flags
 
-         // bit: 7-0; mask 0x00FF; column index
 
-         $fc = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($subData, 4)) + 1);
 
-         // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
 
-         if (!(0x4000 & self::getUInt2d($subData, 4))) {
 
-             $fc = '$' . $fc;
 
-         }
 
-         // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
 
-         if (!(0x8000 & self::getUInt2d($subData, 4))) {
 
-             $fr = '$' . $fr;
 
-         }
 
-         // offset: 6; size: 2; index to last column or column offset + relative flags
 
-         // bit: 7-0; mask 0x00FF; column index
 
-         $lc = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($subData, 6)) + 1);
 
-         // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
 
-         if (!(0x4000 & self::getUInt2d($subData, 6))) {
 
-             $lc = '$' . $lc;
 
-         }
 
-         // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
 
-         if (!(0x8000 & self::getUInt2d($subData, 6))) {
 
-             $lr = '$' . $lr;
 
-         }
 
-         return "$fc$fr:$lc$lr";
 
-     }
 
-     /**
 
-      * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column
 
-      * to indicate offsets from a base cell
 
-      * section 3.3.4.
 
-      *
 
-      * @param string $subData
 
-      * @param string $baseCell Base cell
 
-      *
 
-      * @return string Cell range address
 
-      */
 
-     private function readBIFF8CellRangeAddressB($subData, $baseCell = 'A1')
 
-     {
 
-         list($baseCol, $baseRow) = Coordinate::coordinateFromString($baseCell);
 
-         $baseCol = Coordinate::columnIndexFromString($baseCol) - 1;
 
-         // TODO: if cell range is just a single cell, should this funciton
 
-         // not just return e.g. 'A1' and not 'A1:A1' ?
 
-         // offset: 0; size: 2; first row
 
-         $frIndex = self::getUInt2d($subData, 0); // adjust below
 
-         // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767)
 
-         $lrIndex = self::getUInt2d($subData, 2); // adjust below
 
-         // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
 
-         if (!(0x4000 & self::getUInt2d($subData, 4))) {
 
-             // absolute column index
 
-             // offset: 4; size: 2; first column with relative/absolute flags
 
-             // bit: 7-0; mask 0x00FF; column index
 
-             $fcIndex = 0x00FF & self::getUInt2d($subData, 4);
 
-             $fc = Coordinate::stringFromColumnIndex($fcIndex + 1);
 
-             $fc = '$' . $fc;
 
-         } else {
 
-             // column offset
 
-             // offset: 4; size: 2; first column with relative/absolute flags
 
-             // bit: 7-0; mask 0x00FF; column index
 
-             $relativeFcIndex = 0x00FF & self::getInt2d($subData, 4);
 
-             $fcIndex = $baseCol + $relativeFcIndex;
 
-             $fcIndex = ($fcIndex < 256) ? $fcIndex : $fcIndex - 256;
 
-             $fcIndex = ($fcIndex >= 0) ? $fcIndex : $fcIndex + 256;
 
-             $fc = Coordinate::stringFromColumnIndex($fcIndex + 1);
 
-         }
 
-         // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
 
-         if (!(0x8000 & self::getUInt2d($subData, 4))) {
 
-             // absolute row index
 
-             $fr = $frIndex + 1;
 
-             $fr = '$' . $fr;
 
-         } else {
 
-             // row offset
 
-             $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536;
 
-             $fr = $baseRow + $frIndex;
 
-         }
 
-         // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
 
-         if (!(0x4000 & self::getUInt2d($subData, 6))) {
 
-             // absolute column index
 
-             // offset: 6; size: 2; last column with relative/absolute flags
 
-             // bit: 7-0; mask 0x00FF; column index
 
-             $lcIndex = 0x00FF & self::getUInt2d($subData, 6);
 
-             $lc = Coordinate::stringFromColumnIndex($lcIndex + 1);
 
-             $lc = '$' . $lc;
 
-         } else {
 
-             // column offset
 
-             // offset: 4; size: 2; first column with relative/absolute flags
 
-             // bit: 7-0; mask 0x00FF; column index
 
-             $relativeLcIndex = 0x00FF & self::getInt2d($subData, 4);
 
-             $lcIndex = $baseCol + $relativeLcIndex;
 
-             $lcIndex = ($lcIndex < 256) ? $lcIndex : $lcIndex - 256;
 
-             $lcIndex = ($lcIndex >= 0) ? $lcIndex : $lcIndex + 256;
 
-             $lc = Coordinate::stringFromColumnIndex($lcIndex + 1);
 
-         }
 
-         // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
 
-         if (!(0x8000 & self::getUInt2d($subData, 6))) {
 
-             // absolute row index
 
-             $lr = $lrIndex + 1;
 
-             $lr = '$' . $lr;
 
-         } else {
 
-             // row offset
 
-             $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536;
 
-             $lr = $baseRow + $lrIndex;
 
-         }
 
-         return "$fc$fr:$lc$lr";
 
-     }
 
-     /**
 
-      * Read BIFF8 cell range address list
 
-      * section 2.5.15.
 
-      *
 
-      * @param string $subData
 
-      *
 
-      * @return array
 
-      */
 
-     private function readBIFF8CellRangeAddressList($subData)
 
-     {
 
-         $cellRangeAddresses = [];
 
-         // offset: 0; size: 2; number of the following cell range addresses
 
-         $nm = self::getUInt2d($subData, 0);
 
-         $offset = 2;
 
-         // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses
 
-         for ($i = 0; $i < $nm; ++$i) {
 
-             $cellRangeAddresses[] = $this->readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8));
 
-             $offset += 8;
 
-         }
 
-         return [
 
-             'size' => 2 + 8 * $nm,
 
-             'cellRangeAddresses' => $cellRangeAddresses,
 
-         ];
 
-     }
 
-     /**
 
-      * Read BIFF5 cell range address list
 
-      * section 2.5.15.
 
-      *
 
-      * @param string $subData
 
-      *
 
-      * @return array
 
-      */
 
-     private function readBIFF5CellRangeAddressList($subData)
 
-     {
 
-         $cellRangeAddresses = [];
 
-         // offset: 0; size: 2; number of the following cell range addresses
 
-         $nm = self::getUInt2d($subData, 0);
 
-         $offset = 2;
 
-         // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses
 
-         for ($i = 0; $i < $nm; ++$i) {
 
-             $cellRangeAddresses[] = $this->readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6));
 
-             $offset += 6;
 
-         }
 
-         return [
 
-             'size' => 2 + 6 * $nm,
 
-             'cellRangeAddresses' => $cellRangeAddresses,
 
-         ];
 
-     }
 
-     /**
 
-      * Get a sheet range like Sheet1:Sheet3 from REF index
 
-      * Note: If there is only one sheet in the range, one gets e.g Sheet1
 
-      * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets,
 
-      * in which case an Exception is thrown.
 
-      *
 
-      * @param int $index
 
-      *
 
-      * @throws Exception
 
-      *
 
-      * @return false|string
 
-      */
 
-     private function readSheetRangeByRefIndex($index)
 
-     {
 
-         if (isset($this->ref[$index])) {
 
-             $type = $this->externalBooks[$this->ref[$index]['externalBookIndex']]['type'];
 
-             switch ($type) {
 
-                 case 'internal':
 
-                     // check if we have a deleted 3d reference
 
-                     if ($this->ref[$index]['firstSheetIndex'] == 0xFFFF or $this->ref[$index]['lastSheetIndex'] == 0xFFFF) {
 
-                         throw new Exception('Deleted sheet reference');
 
-                     }
 
-                     // we have normal sheet range (collapsed or uncollapsed)
 
-                     $firstSheetName = $this->sheets[$this->ref[$index]['firstSheetIndex']]['name'];
 
-                     $lastSheetName = $this->sheets[$this->ref[$index]['lastSheetIndex']]['name'];
 
-                     if ($firstSheetName == $lastSheetName) {
 
-                         // collapsed sheet range
 
-                         $sheetRange = $firstSheetName;
 
-                     } else {
 
-                         $sheetRange = "$firstSheetName:$lastSheetName";
 
-                     }
 
-                     // escape the single-quotes
 
-                     $sheetRange = str_replace("'", "''", $sheetRange);
 
-                     // if there are special characters, we need to enclose the range in single-quotes
 
-                     // todo: check if we have identified the whole set of special characters
 
-                     // it seems that the following characters are not accepted for sheet names
 
-                     // and we may assume that they are not present: []*/:\?
 
-                     if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/u", $sheetRange)) {
 
-                         $sheetRange = "'$sheetRange'";
 
-                     }
 
-                     return $sheetRange;
 
-                     break;
 
-                 default:
 
-                     // TODO: external sheet support
 
-                     throw new Exception('Xls reader only supports internal sheets in formulas');
 
-                     break;
 
-             }
 
-         }
 
-         return false;
 
-     }
 
-     /**
 
-      * read BIFF8 constant value array from array data
 
-      * returns e.g. ['value' => '{1,2;3,4}', 'size' => 40]
 
-      * section 2.5.8.
 
-      *
 
-      * @param string $arrayData
 
-      *
 
-      * @return array
 
-      */
 
-     private static function readBIFF8ConstantArray($arrayData)
 
-     {
 
-         // offset: 0; size: 1; number of columns decreased by 1
 
-         $nc = ord($arrayData[0]);
 
-         // offset: 1; size: 2; number of rows decreased by 1
 
-         $nr = self::getUInt2d($arrayData, 1);
 
-         $size = 3; // initialize
 
-         $arrayData = substr($arrayData, 3);
 
-         // offset: 3; size: var; list of ($nc + 1) * ($nr + 1) constant values
 
-         $matrixChunks = [];
 
-         for ($r = 1; $r <= $nr + 1; ++$r) {
 
-             $items = [];
 
-             for ($c = 1; $c <= $nc + 1; ++$c) {
 
-                 $constant = self::readBIFF8Constant($arrayData);
 
-                 $items[] = $constant['value'];
 
-                 $arrayData = substr($arrayData, $constant['size']);
 
-                 $size += $constant['size'];
 
-             }
 
-             $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"'
 
-         }
 
-         $matrix = '{' . implode(';', $matrixChunks) . '}';
 
-         return [
 
-             'value' => $matrix,
 
-             'size' => $size,
 
-         ];
 
-     }
 
-     /**
 
-      * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value'
 
-      * section 2.5.7
 
-      * returns e.g. ['value' => '5', 'size' => 9].
 
-      *
 
-      * @param string $valueData
 
-      *
 
-      * @return array
 
-      */
 
-     private static function readBIFF8Constant($valueData)
 
-     {
 
-         // offset: 0; size: 1; identifier for type of constant
 
-         $identifier = ord($valueData[0]);
 
-         switch ($identifier) {
 
-             case 0x00: // empty constant (what is this?)
 
-                 $value = '';
 
-                 $size = 9;
 
-                 break;
 
-             case 0x01: // number
 
-                 // offset: 1; size: 8; IEEE 754 floating-point value
 
-                 $value = self::extractNumber(substr($valueData, 1, 8));
 
-                 $size = 9;
 
-                 break;
 
-             case 0x02: // string value
 
-                 // offset: 1; size: var; Unicode string, 16-bit string length
 
-                 $string = self::readUnicodeStringLong(substr($valueData, 1));
 
-                 $value = '"' . $string['value'] . '"';
 
-                 $size = 1 + $string['size'];
 
-                 break;
 
-             case 0x04: // boolean
 
-                 // offset: 1; size: 1; 0 = FALSE, 1 = TRUE
 
-                 if (ord($valueData[1])) {
 
-                     $value = 'TRUE';
 
-                 } else {
 
-                     $value = 'FALSE';
 
-                 }
 
-                 $size = 9;
 
-                 break;
 
-             case 0x10: // error code
 
-                 // offset: 1; size: 1; error code
 
-                 $value = Xls\ErrorCode::lookup(ord($valueData[1]));
 
-                 $size = 9;
 
-                 break;
 
-         }
 
-         return [
 
-             'value' => $value,
 
-             'size' => $size,
 
-         ];
 
-     }
 
-     /**
 
-      * Extract RGB color
 
-      * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4.
 
-      *
 
-      * @param string $rgb Encoded RGB value (4 bytes)
 
-      *
 
-      * @return array
 
-      */
 
-     private static function readRGB($rgb)
 
-     {
 
-         // offset: 0; size 1; Red component
 
-         $r = ord($rgb[0]);
 
-         // offset: 1; size: 1; Green component
 
-         $g = ord($rgb[1]);
 
-         // offset: 2; size: 1; Blue component
 
-         $b = ord($rgb[2]);
 
-         // HEX notation, e.g. 'FF00FC'
 
-         $rgb = sprintf('%02X%02X%02X', $r, $g, $b);
 
-         return ['rgb' => $rgb];
 
-     }
 
-     /**
 
-      * Read byte string (8-bit string length)
 
-      * OpenOffice documentation: 2.5.2.
 
-      *
 
-      * @param string $subData
 
-      *
 
-      * @return array
 
-      */
 
-     private function readByteStringShort($subData)
 
-     {
 
-         // offset: 0; size: 1; length of the string (character count)
 
-         $ln = ord($subData[0]);
 
-         // offset: 1: size: var; character array (8-bit characters)
 
-         $value = $this->decodeCodepage(substr($subData, 1, $ln));
 
-         return [
 
-             'value' => $value,
 
-             'size' => 1 + $ln, // size in bytes of data structure
 
-         ];
 
-     }
 
-     /**
 
-      * Read byte string (16-bit string length)
 
-      * OpenOffice documentation: 2.5.2.
 
-      *
 
-      * @param string $subData
 
-      *
 
-      * @return array
 
-      */
 
-     private function readByteStringLong($subData)
 
-     {
 
-         // offset: 0; size: 2; length of the string (character count)
 
-         $ln = self::getUInt2d($subData, 0);
 
-         // offset: 2: size: var; character array (8-bit characters)
 
-         $value = $this->decodeCodepage(substr($subData, 2));
 
-         //return $string;
 
-         return [
 
-             'value' => $value,
 
-             'size' => 2 + $ln, // size in bytes of data structure
 
-         ];
 
-     }
 
-     /**
 
-      * Extracts an Excel Unicode short string (8-bit string length)
 
-      * OpenOffice documentation: 2.5.3
 
-      * function will automatically find out where the Unicode string ends.
 
-      *
 
-      * @param string $subData
 
-      *
 
-      * @return array
 
-      */
 
-     private static function readUnicodeStringShort($subData)
 
-     {
 
-         $value = '';
 
-         // offset: 0: size: 1; length of the string (character count)
 
-         $characterCount = ord($subData[0]);
 
-         $string = self::readUnicodeString(substr($subData, 1), $characterCount);
 
-         // add 1 for the string length
 
-         $string['size'] += 1;
 
-         return $string;
 
-     }
 
-     /**
 
-      * Extracts an Excel Unicode long string (16-bit string length)
 
-      * OpenOffice documentation: 2.5.3
 
-      * this function is under construction, needs to support rich text, and Asian phonetic settings.
 
-      *
 
-      * @param string $subData
 
-      *
 
-      * @return array
 
-      */
 
-     private static function readUnicodeStringLong($subData)
 
-     {
 
-         $value = '';
 
-         // offset: 0: size: 2; length of the string (character count)
 
-         $characterCount = self::getUInt2d($subData, 0);
 
-         $string = self::readUnicodeString(substr($subData, 2), $characterCount);
 
-         // add 2 for the string length
 
-         $string['size'] += 2;
 
-         return $string;
 
-     }
 
-     /**
 
-      * Read Unicode string with no string length field, but with known character count
 
-      * this function is under construction, needs to support rich text, and Asian phonetic settings
 
-      * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3.
 
-      *
 
-      * @param string $subData
 
-      * @param int $characterCount
 
-      *
 
-      * @return array
 
-      */
 
-     private static function readUnicodeString($subData, $characterCount)
 
-     {
 
-         $value = '';
 
-         // offset: 0: size: 1; option flags
 
-         // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit)
 
-         $isCompressed = !((0x01 & ord($subData[0])) >> 0);
 
-         // bit: 2; mask: 0x04; Asian phonetic settings
 
-         $hasAsian = (0x04) & ord($subData[0]) >> 2;
 
-         // bit: 3; mask: 0x08; Rich-Text settings
 
-         $hasRichText = (0x08) & ord($subData[0]) >> 3;
 
-         // offset: 1: size: var; character array
 
-         // this offset assumes richtext and Asian phonetic settings are off which is generally wrong
 
-         // needs to be fixed
 
-         $value = self::encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed);
 
-         return [
 
-             'value' => $value,
 
-             'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags
 
-         ];
 
-     }
 
-     /**
 
-      * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas.
 
-      * Example:  hello"world  -->  "hello""world".
 
-      *
 
-      * @param string $value UTF-8 encoded string
 
-      *
 
-      * @return string
 
-      */
 
-     private static function UTF8toExcelDoubleQuoted($value)
 
-     {
 
-         return '"' . str_replace('"', '""', $value) . '"';
 
-     }
 
-     /**
 
-      * Reads first 8 bytes of a string and return IEEE 754 float.
 
-      *
 
-      * @param string $data Binary string that is at least 8 bytes long
 
-      *
 
-      * @return float
 
-      */
 
-     private static function extractNumber($data)
 
-     {
 
-         $rknumhigh = self::getInt4d($data, 4);
 
-         $rknumlow = self::getInt4d($data, 0);
 
-         $sign = ($rknumhigh & 0x80000000) >> 31;
 
-         $exp = (($rknumhigh & 0x7ff00000) >> 20) - 1023;
 
-         $mantissa = (0x100000 | ($rknumhigh & 0x000fffff));
 
-         $mantissalow1 = ($rknumlow & 0x80000000) >> 31;
 
-         $mantissalow2 = ($rknumlow & 0x7fffffff);
 
-         $value = $mantissa / pow(2, (20 - $exp));
 
-         if ($mantissalow1 != 0) {
 
-             $value += 1 / pow(2, (21 - $exp));
 
-         }
 
-         $value += $mantissalow2 / pow(2, (52 - $exp));
 
-         if ($sign) {
 
-             $value *= -1;
 
-         }
 
-         return $value;
 
-     }
 
-     /**
 
-      * @param int $rknum
 
-      *
 
-      * @return float
 
-      */
 
-     private static function getIEEE754($rknum)
 
-     {
 
-         if (($rknum & 0x02) != 0) {
 
-             $value = $rknum >> 2;
 
-         } else {
 
-             // changes by mmp, info on IEEE754 encoding from
 
-             // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
 
-             // The RK format calls for using only the most significant 30 bits
 
-             // of the 64 bit floating point value. The other 34 bits are assumed
 
-             // to be 0 so we use the upper 30 bits of $rknum as follows...
 
-             $sign = ($rknum & 0x80000000) >> 31;
 
-             $exp = ($rknum & 0x7ff00000) >> 20;
 
-             $mantissa = (0x100000 | ($rknum & 0x000ffffc));
 
-             $value = $mantissa / pow(2, (20 - ($exp - 1023)));
 
-             if ($sign) {
 
-                 $value = -1 * $value;
 
-             }
 
-             //end of changes by mmp
 
-         }
 
-         if (($rknum & 0x01) != 0) {
 
-             $value /= 100;
 
-         }
 
-         return $value;
 
-     }
 
-     /**
 
-      * Get UTF-8 string from (compressed or uncompressed) UTF-16 string.
 
-      *
 
-      * @param string $string
 
-      * @param bool $compressed
 
-      *
 
-      * @return string
 
-      */
 
-     private static function encodeUTF16($string, $compressed = false)
 
-     {
 
-         if ($compressed) {
 
-             $string = self::uncompressByteString($string);
 
-         }
 
-         return StringHelper::convertEncoding($string, 'UTF-8', 'UTF-16LE');
 
-     }
 
-     /**
 
-      * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8.
 
-      *
 
-      * @param string $string
 
-      *
 
-      * @return string
 
-      */
 
-     private static function uncompressByteString($string)
 
-     {
 
-         $uncompressedString = '';
 
-         $strLen = strlen($string);
 
-         for ($i = 0; $i < $strLen; ++$i) {
 
-             $uncompressedString .= $string[$i] . "\0";
 
-         }
 
-         return $uncompressedString;
 
-     }
 
-     /**
 
-      * Convert string to UTF-8. Only used for BIFF5.
 
-      *
 
-      * @param string $string
 
-      *
 
-      * @return string
 
-      */
 
-     private function decodeCodepage($string)
 
-     {
 
-         return StringHelper::convertEncoding($string, 'UTF-8', $this->codepage);
 
-     }
 
-     /**
 
-      * Read 16-bit unsigned integer.
 
-      *
 
-      * @param string $data
 
-      * @param int $pos
 
-      *
 
-      * @return int
 
-      */
 
-     public static function getUInt2d($data, $pos)
 
-     {
 
-         return ord($data[$pos]) | (ord($data[$pos + 1]) << 8);
 
-     }
 
-     /**
 
-      * Read 16-bit signed integer.
 
-      *
 
-      * @param string $data
 
-      * @param int $pos
 
-      *
 
-      * @return int
 
-      */
 
-     public static function getInt2d($data, $pos)
 
-     {
 
-         return unpack('s', $data[$pos] . $data[$pos + 1])[1];
 
-     }
 
-     /**
 
-      * Read 32-bit signed integer.
 
-      *
 
-      * @param string $data
 
-      * @param int $pos
 
-      *
 
-      * @return int
 
-      */
 
-     public static function getInt4d($data, $pos)
 
-     {
 
-         // FIX: represent numbers correctly on 64-bit system
 
-         // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
 
-         // Changed by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
 
-         $_or_24 = ord($data[$pos + 3]);
 
-         if ($_or_24 >= 128) {
 
-             // negative number
 
-             $_ord_24 = -abs((256 - $_or_24) << 24);
 
-         } else {
 
-             $_ord_24 = ($_or_24 & 127) << 24;
 
-         }
 
-         return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
 
-     }
 
-     private function parseRichText($is)
 
-     {
 
-         $value = new RichText();
 
-         $value->createText($is);
 
-         return $value;
 
-     }
 
- }
 
 
  |