مقاله فنی

جاسازی آبجکت‌های نمودار روی کاربرگ‌ها با HotXLS

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، که همان چیزی است که نمودار جاسازی‌شده را وقتی کاربر رویش کلیک می‌کند شبیه نمودار بومی رفتار می‌کند

HotXLS زیرجریان نمودار BIFF8 را از طریق سه قطعه متوافق به یک کاربرگ Delphi لنگر می‌کند: شکل host-control لایه رسم، رکورد OBJ که داده آبجکت عمومی‌اش نوع آبجکت 5 را اعلام می‌کند، و زنجیره رکورد نمودار که در انتهای جریان رکورد شیت پارک شده، جایی که یک BOF نمودار زمینه parser را عوض می‌کند و EOF بسته‌شده رکوردها را دوباره وصل می‌کند
سه لایه یک نمودار جاسازی‌شده را حمل می‌کنند: شکل رسم لنگرش می‌کند، رکورد OBJ تایپش می‌کند به‌عنوان میزبان نمودار، و زیرجریان نمودار در انتهای جریان شیت رکوردهایی را فراهم می‌کند که reader دوباره وصل می‌کند

لنگر ارزش یک یادداشت دارد چون منبع رایج باگ‌های 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 می‌شود، پس نمودار تمیز نوشته می‌شود ولی آن سری خاص تا وقتی به بازه‌ای اشاره‌اش ندهید منبع داده‌ای ندارد

پذیرش ارجاع سری در HotXLS روی مسیر نمودار جاسازی‌شده BIFF8: بازه‌های به سبک A1 مثل Data!$B$2:$B$13 درون همان workbook در برابر لیست شیت‌ها به شاخص ارجاع خارجی حل می‌شوند که رکوردهای نمودار لازم دارند، در حالی که named rangeها و ارجاع‌های workbook خارجی به placeholderای با عبارت parse‌شده صفر-طول fallback می‌شوند، و هر دو روی AddChartSheet پوشش داده شده‌اند
فقط بازه‌های ساده به سبک A1 درون همان workbook به ارجاع‌های سری نمودار کامپایل می‌شوند؛ بقیه تا وقتی دوباره اشاره داده شوند تمیز به‌عنوان placeholder نوشته می‌شوند، و مسیر کامل روی AddChartSheet است

دلیلش یک معامله مهندسی مستقیم است. مسیر کامل کامپایل ارجاع روی مسیر 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 را هم راضی کند