HotXLS sprema odabire radnog lista i scroll pozicije po paneu kroz jedan pane-aware API, na TXLSWorksheet i TXLSXWorksheet jednako: SelectAreas, GetSelectedAreas, ScrollWindow i TryGetWindowScroll. Za klasične .xls datoteke HotXLS piše BIFF8 Selection zapise (0x001D) s najviše 1369 područja svaki, logička imena paneova pretvara u pane bajtove koje format definira, a svaku scroll osu drži na Window2 ili Pane zapisu gdje je Excel i očekuje
Problem se obično pojavi u alatu za usklađivanje ili reviziju. Alat otvori izvoz glavne knjige, nađe svaku ćeliju koja se ne slaže s izvornim sustavom i spremi radnu knjigu s tim ćelijama već odabranim ispod zamrznutog reda zaglavlja, pa revizor slijeti ravno na razlike umjesto da ih traži skrolajući. S četrdeset razlika radi lijepo. Zaključna datoteka mjeseca ima 3.000, a jedan Selection zapis s 3.000 područja ne može postojati: tijelo bi mu trebalo 18.009 bajtova, više nego dvostruko od onoga što jedan BIFF8 zapis može nositi. Scroll pozicija ima sličnu zamku. Na listu sa zamrznutim paneovima "gdje je korisnik gledao" su četiri panea koja dijele dvije pozicije redaka i dvije pozicije stupaca, ne jedna koordinata
Zašto veliki odabir treba više od jednog Selection zapisa?
Veliki odabir treba više zapisa jer je tijelo BIFF8 zapisa ograničeno na 8224 bajtova, a svako odabrano područje košta fiksiranih šest bajtova. [MS-XLS] §2.4.248 razlaže Selection zapis kao 9-bajtni fiksni dio (pane bajt, rwAct i colAct za aktivnu ćeliju, irefAct za aktivno područje i cref za broj područja) nakon kojeg slijedi cref RefU struktura, svaka s dva 16-bitna retka i dva 8-bitna stupca. Najveći broj koji stane je (8224 − 9) / 6 zaokruženo dolje, što je 1369, i to daje tijelo od 8223 bajta, jedan bajt ispod granice. TXLSWorksheet.StoreSelectionGroup tu konstantu koristi kao MaxAreasPerRecord i veću grupu piše kao uzastopne Selection zapise za isti pane, po 1369 područja
Detalj koji grize je irefAct. Svaki komad ponavlja isti aktivni redak, aktivni stupac i indeks aktivnog područja, a irefAct indeksira zbirni slijed svih komada, ne područja unutar zapisa koji ga nosi. Odabir jedno područje preko granice to čini konkretnim: 1370 područja s posljednjim aktivnim postaje dva zapisa, prvi s cref 1369 i drugi s cref 1, i oba nose irefAct 1369. Ta je vrijednost veća od vlastitog broja područja drugog zapisa. Čitač koji provjerava irefAct protiv cref u svakom zapisu odbacuje valjanu datoteku, a čitač koji stanje mijenja na svakom zapisu gubi prvih 1369 područja. HotXLS čitač uzastopne zapise istog panea dodaje u jednu grupu, zahtijeva da se svaki komad slaže oko aktivne ćelije i indeksa, a provjeru raspona pušta tek na EOF zapisu radnog lista, kad je cijeli slijed poznat. Pane-prvi overload SelectAreas zato nema 1369-granicu. Provjerava svaku A1 referencu i aktivni indeks prije nego uzme write lock radnog lista, i vraća False s prethodnim odabirom nepromijenjenim ako je bilo što neispravno
var
Book: TXLSWorkbook;
Sheet: TXLSWorksheet;
Diffs: TXLSSelectedAreas;
I: Integer;
begin
Book := TXLSWorkbook.Create;
try
Sheet := Book.Sheets.Add;
Sheet.FreezePanes(1, 1); // red zaglavlja i stupac A stoje na mjestu
SetLength(Diffs, 3000);
for I := 0 to High(Diffs) do
Diffs[I] := Format('C%d', [I + 2]);
// Zamrzavanje resetira spremljeni odabir, pa odabirite nakon zamrzavanja.
// 3000 područja sprema se kao tri Selection zapisa: 1369 + 1369 + 262
if not Sheet.SelectAreas(xlspBottomRight, Diffs, 0) then
raise Exception.Create('Selection rejected');
Book.SaveAs('reconciliation.xls');
finally
Book.Free;
end;
end;
Koji pane bajt Selection zapis koristi?
Selection zapis svoj pane identificira numeričkim kodom koji format definira: 0 za donji desni, 1 za gornji desni, 2 za donji lijevi i 3 za gornji lijevi. Javna enumeracija TXLSPanePosition deklarirana je u redoslijedu čitanja, xlspTopLeft, xlspTopRight, xlspBottomLeft, xlspBottomRight, pa je Ord(xlspTopLeft) 0, što je u datoteci donji desni pane. Cast enumeracije ravno u pane bajt upisao bi svaki gornji lijevi odabir na donji desni pane bez ijedne greške. Svaki pane-aware ulaz HotXLS-a konvertira enumeraciju kroz eksplicitnu case naredbu, pa pozivatelji nikad ne barataju numeričkim kodovima. Provjerava se i postojanje panea: gornji desni postoji samo uz vertikalni split, donji lijevi samo uz horizontalni, a donji desni samo uz oba. Za pane koji trenutna geometrija splita ili zamrzavanja nema, SelectAreas vraća False, a GetSelectedAreas prazan niz s ActiveAreaIndex postavljenim na -1, bez stvaranja panea, objekta odabira ili ijedne ćelije u radnoj knjizi
Gdje živi scroll pozicija svakog panea?
Scroll pozicija svakog panea raspršena je po dva zapisa, jer četiri panea dijele samo dvije pozicije redaka i dvije pozicije stupaca. U klasičnoj radnoj knjizi prvi vidljivi redak gornjih paneova i prvi vidljivi stupac lijevih paneova su Window2.rwTop i Window2.colLeft, dok su redak donjih i stupac desnih paneova Pane.rwTop i Pane.colLeft. ScrollWindow(xlspTopRight, R, C) zato piše Window2.rwTop i Pane.colLeft, i postavljanje stupca gornjeg desnog panea pomiče i donji desni, isto kao što dva dijele jednu horizontalnu scroll traku u Excelu. Javne metode koriste brojeve redaka i stupaca od 1. Nepostojeći pane vraća False i oba izlaza upita postavlja na nulu, a koordinata izvan raspona odbija se prije nego ijedna os promijeni. Ništa od ovoga ne ovisi o tome kako viewer crta grid. Rendering kontrola drži svoje TopRow i LeftCol, kako to opisuje članak o renderanju radnih knjiga u vlastitom VCL gridu, i to je runtime stanje, ne ono što se sprema
XLSX iste podatke rasprostru preko dva elementa: sheetView/@topLeftCell (ECMA-376 Part 1, §18.3.1.87) za prozor u cijelosti i dijete pane/@topLeftCell (§18.3.1.66) za donju desnu stranu splita. Oba atributa mogu biti prisutna istovremeno. HotXLS prvo čita vanjski atribut u polja razine prozora, pušta da pane dijete nadjača samo polja razine panea, i oboje zapisuje odvojeno. Spajanje ta dva sloja u jedan je upravo način na koji gornja ili lijeva scroll pozicija tiho nestaje pri učitavanju. Kopije radnih lista nose oba sloja u oba enginea. Stariji ulazi zadržavaju izvorno ponašanje: klasična svojstva ScrollRow i ScrollColumn, te nula-bazirani XLSX SetPaneScroll i GetPaneScroll. Samu geometriju zamrzavanja i splita podešava se postavkama razine lista obrađenima u zaštiti lista, postavama stranice i ispisu
var
Row, Col: Integer;
begin
Sheet.FreezePanes(1, 1);
// Donji desni: os donjih redaka (Pane.rwTop) i os desnih stupaca (Pane.colLeft)
Sheet.ScrollWindow(xlspBottomRight, 500, 3);
// Gornji desni dijeli os desnih stupaca, pa ovo pomiče i donji desni na stupac 6
Sheet.ScrollWindow(xlspTopRight, 1, 6);
if Sheet.TryGetWindowScroll(xlspBottomRight, Row, Col) then
Memo1.Lines.Add(Format('Bottom-right starts at row %d, column %d', [Row, Col]));
// Donji desni kreće od retka 500, stupca 6
end;
Što se događa kad je Selection zapis oštećen?
Kad je Selection zapis oštećen, HotXLS ga drži kao neprozirne bajtove, javlja dijagnostički kod 1304 (xlsDiagnosticSelectionRecordInvalid) i pri spremanju ispisuje izvorno tijelo bajt po bajt. Prije nego zapis uđe u grupu svog panea, čitač ga provjerava redom. Pane bajt mora biti 3 ili manje. Zapisi jednog panea moraju biti kontinuirani u streamu. Fiksnih 9 bajtova mora biti prisutno. cref mora biti između 1 i 1369, a tijelo mora biti točno 9 + cref × 6 bajtova dugo. Svaki komad u grupi mora se slagati oko aktivne ćelije i irefAct, irefAct ne smije imati postavljen znakovni bit, aktivni stupac mora biti na gridu, i nijedno područje ne smije imati obrnute granice. Problemi u jednom fizičkom zapisu javljaju se jednom po zapisu. Proturječja koja se pojave tek nakon agregacije, poput irefAct koji pokazuje preko ukupnog broja područja ili aktivne ćelije izvan indeksiranog područja, javljaju se jednom po grupi na EOF. Nevaljana grupa ostaje nevidljiva tipiziranom API-ju: GetSelectedAreas za taj pane vraća prazan niz s indeksom -1, dok svi ostali paneovi nastavljaju raditi
var
I: Integer;
D: TXLSDiagnostic;
begin
if Book.Open('supplier-upload.xls') <> 1 then
Exit;
for I := 0 to Book.Diagnostics.Count - 1 do
begin
D := Book.Diagnostics[I];
if D.Code = xlsDiagnosticSelectionRecordInvalid then
Log.Add(Format('%s: record $%.4x kept opaque (%s)',
[D.SheetName, D.RecordId, D.Message]));
end;
end;
Kako odabiri prežive umetanja redaka i stupaca?
Odabiri preživljavaju strukturne izmjene jer umetanje ili brisanje cijelih redaka ili stupaca remapira svaku zastupljenu pane grupu u Classic i XLSX engineu kroz jedan zajednički remapper. Područja koja prežive zadržavaju redoslijed, a aktivno područje zadržava svoj identitet. Ako se aktivno područje izbriše, aktivnim postaje prvi preživjeli sljedbenik, a ako ništa ne slijedi, zadnji preživjeli prethodnik. Ako se izbrišu sva područja, grupa se sažme u jednu ćeliju na granici brisanja, a aktivna ćelija koja više ne pada unutar odabranog područja seli u gornji lijevi kut tog područja, pa se indeks i koordinata nikad ne proturječe. Granice su namjerne. Nevaljane Classic grupe remapper preskače umjesto da ih prepiše u izmišljeni odabir, pa njihovi izvorni bajtovi i dalje prolaze round trip. Uređivanje jednog panea mijenja samo zapise tog panea i ostavlja ostale identične do bajta. ODS uopće ne dobiva stanje pane odabira, jer ODF nema ekvivalentnu strukturu prikaza radnog lista koja bi ga nosila
Ako Vaša aplikacija piše .xls datoteke koje korisnici otvaraju i trebaju navigirati — bilo da pregledavaju označene ćelije, nastavljaju odakle su stali ili dijele zamrznuti dashboard — pane-aware API odabira i scrola dio je HotXLS Delphi spreadsheet komponente, i radi isto za XLS i XLSX