Tekninen artikkeli

HotXLS: taulukon suojaus, sivun asetukset ja tulostus Delphissä

Kolme laskentataulukon asetusryhmää eivät liity mitenkään soluarvoihin, vaan kaikkeen siihen, miten tiedosto toimii poistuttuaan koodistasi. Taulukon suojaus määrittää, mitä soluja käyttäjä voi muokata, kun työkirja luovutetaan eteenpäin. Sivun asetukset määrittävät suunnan, paperikoon ja marginaalit. Tulostusasetukset (toistuvat otsikkorivit, skaalaus ja manuaaliset sivunvaihdot) ohjaavat sitä, miten mielivaltaisen pituinen ruudukko päätyy paperille. Mikään näistä kolmesta ei näy, kun tietoja silmäilee katseluohjelmassa, ja jokainen niistä rikkoutuu hiljaisesti käytössä, jos se on väärin. HotXLS, Delphin ja C++Builderin natiivi laskentataulukkokirjasto, tarjoaa täydellisen rajapinnan .xls- ja .xlsx-tiedostoille, joten se toistaa myös kaikki tähän rajapintaan sisäänrakennetut Excelin epäintuitiiviset säännöt

Ensimmäinen näistä säännöistä yllättää lähes kaikki, kun he suojaavat tuotetun taulukon ensimmäisen kerran. Kutsu Protect-metodia, ja yhtäkkiä kukaan ei voi kirjoittaa mihinkään soluun, edes niihin syötesarakkeisiin, joiden varaan työkirja rakennettiin. Koodisi ei koskenut niihin sarakkeisiin, ja juuri siksi näin tapahtuu

Jokainen solu syntyy lukittuna

ECMA-376 määrittelee locked-asetuksen osaksi solun muotoilutietuetta eikä suojauksen ominaisuudeksi, ja sen oletusarvo on true. Taulukon suojaus on vain kytkin, joka tekee lipusta toimeenpantavan. Koko ruudukossa on siis lukituslippu sen syntyhetkestä alkaen lepotilassa, ja Protect-kutsu aktivoi ne kaikki kerralla. Korjaus on määrittää järjestys tarkoituksella: rakenna asettelu, avaa nimenomaisesti lukitus niiltä alueilta, joita käyttäjien on muokattava, ja suojaa vasta lopuksi

Kaavio HotXLS:n suojausjärjestyksestä Delphissä, jossa jokainen solu syntyy locked true -arvolla, syötealueet avataan SetLockedilla ensin, ja viimeisenä kutsuttu Sheet.Protect pitää avatut solut muokattavina
Solut saapuvat lukittuina oletuksena, joten avaa syötealueiden lukitukset ensin ja kutsu Protectia viimeisenä, jotta ne pysyvät muokattavissa
Book := TXLSXWorkbook.Create;
try
  Sheet := Book.Sheets.Add('Timesheet');
  // ... otsikkorivi, nimisarake ja kurssikaavat kirjoitetaan tähän ...
  Sheet.Range['B2:B50'].SetLocked(False);         // henkilöstötyypin tunnit tässä
  Sheet.Range['F2:F50'].SetFormulaHidden(True);   // pidä kurssilaskenta yksityisenä
  Sheet.Protect('review-2026');                   // nyt lukitusliput astuvat voimaan
  Book.SaveAs('timesheet.xlsx');
finally
  Book.Free;
end;

SetFormulaHidden tekee jotain erillistä, mikä jää helposti huomaamatta: kun suojaus on käytössä, solu näyttää edelleen lasketun arvonsa, mutta kaavarivi ei näytä mitään. Tämä on tärkeää, kun kaava sisältää laskutusveloituksia, katteita tai pisteytyspainoja, joita et halua antaa jokaiselle vastaanottajalle, joka napsauttaa yhteissummaa. XLS-rajapinnassa sama tarkoitus ilmaistaan aluekohtaisesti IXLSRange.Locked- ja FormulaHidden-asetuksilla. Siellä laskentataulukossa on myös viisitoista Allow*-lippua (AllowSort, AllowAutoFilter, AllowFormatCells ja muut), joten suojattu taulukko voidaan silti lajitella ja suodattaa sen sijaan, että se jäädytettäisiin sinetöidyksi näyttelyesineeksi

Mitä suojauksen salasana todella suojaa

Molemmat muodot tallentavat taulukon ja työkirjan suojauksen salasanan 4 heksadesimaalin merkin pituisena vanhanaikaisena tiivisteenä. Kuusitoista bittiä tarkoittaa, että lukemattomat merkkijonot törmäävät mihin tahansa tiettyyn salasanaan, ja poistotyökalut ovat yhden haun päässä. Käsittele suojausta turvavyönä vahinkomuokkauksia vastaan, ei pääsynhallintana. Se on oikea työkalu estämään tarkistajia kirjoittamasta kaavasarakkeen päälle ja väärä työkalu kaikkeen, missä esiintyy sana luottamuksellinen

Yhtä tasoa ylempänä XLSX-rajapinnan ProtectWorkbook lukitsee työkirjan rakenteen, mikä estää lisäämästä, nimeämästä uudelleen, poistamasta tai järjestämästä uudelleen laskentataulukoita. Aseta se aina, kun taulukkoluettelo on itsessään sopimus jatkojäsentäjän kanssa, joka indeksoi taulukot nimen tai sijainnin perusteella. Uudelleennimetty taulukko rikkoo toisessa päässä olevan tuonnin yhtä varmasti kuin poistettu sarake. XLS-rajapinta vastaa kerrostukseen työkirjatason TXLSWorkbook.Protect-metodilla ja taulukkokohtaisilla Protect-kutsuilla sekä isProtected-ominaisuudella koodille, jonka on tutkittava peritty tiedosto ennen kuin se muokkaa mitään

Kun vaatimuksena on todellinen luottamuksellisuus, mekanismi muuttuu kokonaan. SaveAsEncrypted tuottaa AES-salatun paketin ECMA-376 Standard Encryption -järjestelmällä, jota käsitellään perusteellisesti AES-suojattujen XLSX-tulosteiden oppaassa, ja vanha XLS-rajapinta kirjoittaa ja lukee RC4-salattuja .xls-tiedostoja EncryptionPassword-asetuksella sekä Open-metodin salasanaylikuormituksella. Ero ei ole akateeminen. Suojattu taulukko kulkee selväkielisenä, joten mikä tahansa zip-työkalu voi lukea sen soluarvot, kun taas salattua pakettia ei voi lukea ilman salasanaa. Auditointirivi, jossa sanotaan "palkkatiedosto on suojattava", tarkoittaa lähes aina salausta riippumatta siitä, mitä sanastoa siinä satutaan käyttämään

Kaavio vertailee HotXLS-taulukkosuojausta, joka tallentaa 16-bittisen vanhan tiivistelmän ja jättää soluarvot selkotekstinä minkä tahansa zip-työkalun luettaviksi, ja SaveAsEncrypted AES -tuotosta, joka pysyy lukukelvottomana ilman salasanaa
Taulukkosuoja on turvavyö vahingossa tapahtuvia muokkauksia vastaan, kun taas selkotekstiarvot pysyvät luettavina, ja vain AES-salaus kätkee sisällön

Sivun asetukset ovat osa asiakirjasopimusta

Tulostuskäyttäytyminen on näytöllä näkymätöntä, minkä vuoksi se toimitetaan niin usein rikkinäisenä. Heti kun asiakas tulostaa työkirjan tai vie sen PDF-muotoon tarkastajaa varten, marginaaleista, skaalauksesta ja toistuvista otsikoista tulee toiminnallisia vaatimuksia, joita kukaan ei testannut. XLSX-rajapinnassa nämä asetukset ovat suoraan laskentataulukossa:

Sheet.PageLandscape := True;
Sheet.PaperSize := xlsxPaperA4;
Sheet.SetPageMargins(0.5, 0.5, 0.75, 0.75, 0.3, 0.3);
Sheet.CenterHeader := 'Monthly Timesheet';
Sheet.RightFooter := 'Page &P of &N';
Sheet.PrintArea := '$A$1:$F$60';     // paljas viittaus: ei arkin nimeä tässä
Sheet.PrintTitleRows := '$1:$1';     // otsikkorivi toistuu jokaisella sivulla
Sheet.FitToWidth := 1;
Sheet.FitToHeight := 0;              // kasva alaspäin datan kasvaessa
Sheet.PrintGridlines := False;

Kaksi näistä riveistä kätkee ansan. Ylätunniste- ja alatunnistemerkkijonot käyttävät Excelin muotoilukoodeja: &P nykyiselle sivulle, &N kokonaissivumäärälle sekä &L, &C ja &R kolmen osion nimenomaiseen osoittamiseen. Toinen ansa on PrintArea, joka ottaa tarkoituksella pelkän solureferenssin. HotXLS tallentaa sen ilman tarkennetta ja lisää taulukon nimen tiedostoa kirjoittaessaan, joten jos annat itse arvon 'Timesheet!$A$1:$F$60', tuloksena on kahdesti tarkennettu, virheellinen viittaus. Sama varovaisuus pätee yhtä tasoa alempana: tulostusalueet ja tulostusotsikot säilytetään sisäänrakennettuina määriteltyinä niminä _xlnm.Print_Area ja _xlnm.Print_Titles, joten älä koskaan lisää _xlnm.*-merkintöjä käsin DefinedNames-kokoelman kautta, tai kaksi mekanismia kilpailee samasta paikasta

Skaalaus, joka kestää tuotantodatan määrät

Yhdistelmä FitToWidth := 1 ja FitToHeight := 0 tarkoittaa "sovita sarakkeet aina yhdelle sivulle ja käytä sitten niin monta sivua pystysuunnassa kuin data tarvitsee", ja se on oikea oletus kaikille raporteille, joiden rivimäärä vaihtelee. Ansana on säätää kiinteä prosenttiosuus tai sovita-sivulle-pari 30 rivin testitiedostolla: anna samoille asetuksille 600 tuotantoriviä, ja tuloste joko leviää kymmeniksi rajatuiksi sivuiksi tai kutistuu lukukelvottomaksi. Skaalaa leveys, anna pituuden kasvaa ja toista otsikkorivi PrintTitleRows-asetuksella, jotta sivu 17 on edelleen luettavissa itsenäisesti

Kaavio HotXLS:n tulostusskaalauksesta Delphissä, jossa FitToWidth asetettuna arvoon 1 pitää jokaisen sivun yhden taulukon levyisenä, FitToHeight arvossa 0 antaa sivujen kasvaa alaspäin, PrintTitleRows toistaa otsikkokaistan, ja sivunvaihdot luodaan uudelleen ClearAllPageBreaksin jälkeen
FitToWidth 1 ja FitToHeight 0 pitävät jokaisen sivun yhden arkin levyisenä, kun taas toistuvat otsikkorivit ja uudelleen luodut sivunvaihdot säilyttävät luettavuuden

Manuaaliset vaihdot noudattavat samaa uudelleentuottamisen kurinalaisuutta kuin kaikki muukin tuotetussa työkirjassa. AddRowBreak(BeforeRow) aloittaa uuden sivun ennen osion rajaa, mutta kun generaattori suoritetaan uudelleen ja rivit siirtyvät, vanhentunut vaihto päätyy keskelle taulukkoa. Kutsu ensin ClearAllPageBreaks ja lisää sitten uudelleen generaattorin omista rivinlaskureista lasketut vaihdot sen sijaan, että paikkaisit vanhoja sijainteja. XLS-rajapinnassa vastaavat säätimet ovat Sheet.PageSetup-objektissa (suunta, paperikoko, marginaalit, ylä- ja alatunnistemerkkijonot, sovitus sivuille), ja RepeatRows sekä RepeatColumns kattavat tulostusotsikot

Tuloksen tarkistaminen ennen asiakasta

Suojaus- ja tulostusvirheillä on yhteinen ominaisuus: ne on helppo tarkistaa käsin, mutta niitä ei juuri koskaan tarkisteta. Avaa tuotettu tiedosto Excelissä ja käytä siihen 90 sekuntia. Kirjoita syötesoluun ja varmista, että se hyväksyy näppäinpainalluksen; kirjoita lukittuun soluun ja varmista, että suojauskehote ilmestyy; tarkista, että piilotettu kaava jättää kaavarivin tyhjäksi. Aja sitten tulostuksen esikatselu tuotantokokoisella tietojoukolla, ei 30 rivin näytteellä, ja tarkista sivumäärä, toistuva otsikkorivi ja alatunnisteen numerointi. Esikatselu on vaihe, joka maksaa itsensä takaisin, koska tulostusgeometria riippuu asetuksista, joilla ei ole näytöllä renderöintiä, ja ilman fyysistä tulostinta se on ainoa paikka, jossa skaalausvirhe koskaan tulee näkyväksi

Yksi viimeinen asetus täydentää tarkistuksen. FreezePane(ACol, ARow) pitää otsikkolohkon näkyvissä, kun tarkistaja vierittää. Tämä on näytön käyttäytymistä eikä tulostuskäyttäytymistä, mutta tarkistaja arvioi koko toimituksen kerralla. Ja työkirja, joka aloittaa elämänsä suunnittelijan ylläpitämänä asetteluna, saa suurimman osan tästä valmiina: malliraporttien tuotantotyönkulku säilyttää sivun asetukset mallissa, jossa ihminen on säätänyt ne oikeaa tulostinta vasten, ja jättää koodin täyttämään tiedot sekä ottamaan suojauksen uudelleen käyttöön, kun asettelu vakiintuu

HotXLS on Delphin ja C++Builderin natiivi Object Pascal -laskentataulukkokirjasto; täydellinen suojaus- ja sivuasetus-API:n viite on HotXLS Delphi Component -tuotesivulla