HotXLS میتواند نموداری را مستقیم روی یک کاربرگ بگذارد، لنگرشده به یک بازه سلولی، بهجای آنکه به یک chart sheet اختصاصی برود. به زبان BIFF8 یعنی نوشتن یک شکل رسم با رکورد OBJ از نوع 5 و پارککردن زیرجریان نمودار در انتهای جریان رکورد شیت، که دقیقاً همان چیدمانی است که Excel تولید میکند و دقیقاً همان جایی که reader منتظر پیدایش است
این تمایز برای هر کسی که گزارش عملیاتی تولید میکند مهم است. یک chart sheet خانه خوبی برای یک ویژوال تیتری منفرد است. یک تحلیل ماهانه منطقهای میخواهد نمودار کنار عددهایی که خلاصهشان میکند باشد، در همان شیت، هماندازه با بلوک سلولی که به آن تعلق دارد، تا خواننده یک بار اسکرول کند بهجای عوضکردن تب و گمکردن زمینه
خواندن از قبل بود، نوشتن نبود
ارزش دارد این عدمتقارن را نامگذاری کرد چون شکل کار را میدهد. HotXLS از قبل میتوانست نمودارهای جاسازیشده را بخواند: وقتی جریان رکورد کاربرگ یک BOF علامتخورده بهعنوان زیرجریان نمودار دارد، parser زمینه را عوض میکند، رکوردهای نمودار را جمع میکند، و در EOF بستهشده آنها را به شکل رسمی که رکورد OBJ معرفی کرده بود پس میدهد. آن مسیر با تکتک workbookهایی که Excel ساخته بود و کتابخانه تا به حالا باز کرده ورز داده شده بود
آنچه نبود سمت تولید بود، و پیامد مفیدش این است که writer جدید یک مشخصات دقیق برای هدفگیری داشت: همان چیدمان بایتی را تولید کن که reader موجود از قبل دوباره وصلش میکند. هیچ معیار پذیرش بهتری برای یک قابلیت فرمت باینری وجود ندارد از یک reader مستقل که نوشتهاید و حق نداشتید تغییرش بدهید
یک نمودار جاسازیشده از چه ساخته شده
سه قطعه باید با هم موافق باشند. لایه رسم یک شکل host-control میدهد، لایه آبجکت یک رکورد OBJ میدهد که داده آبجکت عمومیاش نوع آبجکت 5 را اعلام میکند، و جریان رکورد خود زیرجریان نمودار را میدهد. پرچمهای گزینه روی رکورد OBJ همانهایی است که Excel برای یک قاب نمودار مینویسد: positioned، locked، automatic line و automatic fill، که همان چیزی است که نمودار جاسازیشده را وقتی کاربر رویش کلیک میکند شبیه نمودار بومی رفتار میکند
لنگر ارزش یک یادداشت دارد چون منبع رایج باگهای off-by-one است. API HotXLS شماره ردیف و ستون یکمبنا میگیرد، مطابق بقیه کتابخانه، و client anchor که داخل فایل نوشته میشود صفرمبناست. تبدیل درون AddChartObject اتفاق میافتد، پس callerها در همان دستگاه مختصاتی میمانند که همهجا دیگر استفاده میکنند، ولی هر کسی که یک hex dump را با فراخوانی خودش مقایسه میکند باید یادش بماند کدام سمت آن مرز را دارد میخواند
var
Book: TXLSWorkbook;
Sheet: TXLSWorksheet;
Series: array[0..1] of TXLSChartSeriesInfo;
begin
Book := TXLSWorkbook.Create(nil);
try
Book.LoadFromFile('regional-sales.xls');
Sheet := Book.Sheets[0];
FillChar(Series, SizeOf(Series), 0);
Series[0].Name := 'Actual';
Series[0].Categories := 'Data!$A$2:$A$13';
Series[0].Values := 'Data!$B$2:$B$13';
Series[0].DataLabels.ShowValue := True;
Series[0].HasDataLabels := True;
Series[1].Name := 'Target';
Series[1].Categories := 'Data!$A$2:$A$13';
Series[1].Values := 'Data!$C$2:$C$13';
Series[1].SecondaryAxis := True;
// لنگر شده به E2:M20 در همین شیت، یکمبنا
Sheet.AddChartObject(xlsChartTypeColumn, 'Regional sales',
'Month', 'Amount', Series, 2, 5, 20, 13);
Book.SaveToFile('regional-sales-charted.xls');
finally
Book.Free;
end;
end;
FillChar روی آرایه سری تزئین نیست. TXLSChartSeriesInfo چند زیر-رکورد اختیاری حمل میکند — برچسب داده، استایل بهازای هر سری، خطوط روند و نوارهای خطا — هرکدام پشت یک بولین، و رکوردی که روی stack نیمه مقداردهی شده به emitter پرچمهایی را میدهد که هیچکس ست نکرده. آرایه را صفر کنید، بعد فیلدهایی را که منظورتان است ست کنید
کدام ارجاعهای سری را مسیر جاسازیشده قبول میکند؟
بازههای ساده به سبک A1 درون همان workbook، و این محدودیت عمدی است نه یک سهو. هر ارجاع در برابر لیست شیتهای workbook حل میشود و به شاخص ارجاع خارجی تبدیل میشود که رکوردهای نمودار لازم دارند. یک named range یا ارجاع به workbook خارجی به یک placeholder با عبارت parseشده صفر-طول fallback میشود، پس نمودار تمیز نوشته میشود ولی آن سری خاص تا وقتی به بازهای اشارهاش ندهید منبع دادهای ندارد
دلیلش یک معامله مهندسی مستقیم است. مسیر کامل کامپایل ارجاع روی مسیر chart-sheet وجود دارد، پیچیدهشده درون لایه مجموعه کاربرگها، و بیرونکشیدن تمیز آن یعنی تکرار صد خط منطق حل برای موردی که در عمل نامعمول است. یک نمودار جاسازیشده تقریباً همیشه سلولهای خود شیتش یا یک شیت داده خواهر را رسم میکند. ارجاعهای named و خارجی روی مسیر chart-sheet از طریق AddChartSheet پوشش داده شدهاند، پس هیچچیز ناموجود نیست، فقط از نقطه ورود دیگری میرسد
بقیه مدل سری روی هر دو مسیر یکسان کار میکند. مقیدسازی محور ثانویه، استایل خط و پر و نشانگر بهازای هر سری، خطوط روند، نوارهای خطا و برچسبهای داده همگی بخشی از TXLSChartSeriesInfo هستند و همه به یک شکل emit میشوند، پس تعریف یک نمودار میتواند بین آبجکت جاسازیشده و chart sheet جابهجا شود فقط با تغییر فراخوانی. مکانیک گروه محور پشت پرچم محور ثانویه در گروههای محور ثانویه در نوشتن BIFF پوشش داده شده
چرا عنوان نمودار دو کاراکتر خوانده میشد؟
چون یک شمار کاراکتر پاس داده میشد جایی که شمار بایت انتظار میرفت، و رشتههای یونیکد BIFF نوشتن این اشتباه را آسان و دیدنش را سخت میکنند. یک رشته کوتاه یونیکد BIFF با یک شمار کاراکتر و یک بایت پرچم شروع میشود، و بایت پرچم بیت high-byte را حمل میکند که میگوید payload یکی است بهازای هر کاراکتر یا دوتا. یک payload شانزدهبیتی را با شمار کاراکتر طوری بخوانید که انگار طول بایتی است و دقیقاً نصف رشته را میگیرید: سریای به نام Sales بهصورت Sa برمیگردد، و عنوان نمودار هم به همان شکل بریده میشود چون عنوانها و برچسبهای سری مسیر decode مشترکی دارند
چیزی که این عیب را برجسته میکند این است که سه بار در همان خانواده رکوردها تکرار شد، یک بار در نامهای trendline، یک بار در نامهای pivot chart، و یک بار در عنوانهای نمودار. هر بار شبیه یک باگ تازه در یک قابلیت جدید به نظر میرسید. هر سه همان ضربِ گمشده بودند. قاعدهای که بالاخره پرونده را بست مکانیکی است و باید بدون قضاوت اعمال شود: هر وقت یکی از این رشتهها را میخوانید، اول پرچم high-byte را ببینید و شمار کاراکتر را در پهنای payload ضرب کنید قبل از دست زدن به بافر. جزئیات سطح رکورد در decode شمار کاراکتر XLUnicodeString و پرچم high-byte است
// نمودار جاسازیشده لایه رسم را با تصاویر و شکلها شریک است، پس یک
// رسم موجود روی شیت حفظ میشود. AddChartObject شاخص آبجکت ساختهشده
// را برمیگرداند
var
ObjIndex: Integer;
begin
ObjIndex := Sheet.AddChartObject(xlsChartTypeLine, 'Trend',
'Week', 'Units', Series, 2, 8, 18, 16);
if ObjIndex < 0 then
raise Exception.Create('chart object was not created');
end;
نمودارهای جاسازیشده در برابر گزینهها کجا مینشینند
سه مسیر وجود دارد و هرکدام به سؤال متفاوتی جواب میدهند. یک آبجکت نمودار جاسازیشده تعلقش کنار دادهاش روی کاربرگ است و چیزی است که بیشتر گزارشها میخواهند. یک chart sheet مناسب یک ویژوال ارائه منفرد است و مسیر کامل کامپایل ارجاع را میدهد. حفظ یک نمودار موجود از فایل بارگذاریشده، دستنخورده، جواب درست است وقتی workbook از Excel آمده با قالببندیای که هیچکس نمیخواهد کتابخانه بازتفسیرش کند؛ آن رفتار pass-through در ChartML حفظشده و نمودارهای ترکیبی توصیف شده
چون نمودار جاسازیشده سوار لایه رسم است، با تصاویر و شکلها روی همان شیت همزیستی دارد بهجای جایگزینکردنشان، و مدل کلی آن لایه در نمودارها، تصاویر و drawingها در HotXLS پوشش داده شده. هر سه مسیر همراه HotXLS Delphi spreadsheet component عرضه میشوند، پس انتخاب درباره این است که گزارش چه شکلی باید داشته باشد نه درباره اینکه کتابخانه چه چیزی را میتواند بیان کند
نکته روششناختی همان است که ارزش نگهداشتن دارد. وقتی یک قابلیت فرمت باینری یک reader موجود دارد، writer را بر اساس reader بسازید نه بر اساس برداشت خودتان از مشخصات. reader سالها برخورد با فایلهایی را کدگذاری کرده که برنامههای واقعی واقعاً تولید کردهاند، شامل بخشهایی که مشخصات بصورت مبهم بیانشان کرده، و writerای که آن را راضی کند خیلی محتملتر است که Excel را هم راضی کند