مقاله فنی

HotXLS: محافظت از برگه، تنظیمات صفحه و چاپ در دلفی

سه گروه از تنظیمات برگه‌کار هیچ ارتباطی با مقادیر سلول‌ها ندارند و کاملاً به نحوه رفتار فایل پس از خروج از کد شما مربوط می‌شوند. محافظت از برگه تعیین می‌کند که کاربر پس از تحویل کتاب کار، کدام سلول‌ها را می‌تواند ویرایش کند. تنظیمات صفحه، جهت‌گیری، اندازه کاغذ و حاشیه‌ها را مشخص می‌نماید. تنظیمات چاپ (سطرهای عنوان تکراری، مقیاس‌گذاری و شکست‌های صفحه دستی) نحوه قرارگیری یک جدول با طول دلخواه روی کاغذ را کنترل می‌کنند. هیچ‌یک از این سه مورد هنگام مشاهده داده‌ها در یک نمایشگر نشان داده نمی‌شوند، و هر سه در صورت اشتباه بودن، به آرامی در محیط کار خراب می‌گردند. کتابخانه صفحه‌گسترده بومی دلفی و C++Builder یعنی HotXLS، تمام این سطوح را برای .xls و .xlsx در دسترس قرار می‌دهد، به این معنی که تمام قوانین غیرمنتظره اکسل را که در آن تعبیه شده است نیز بازتولید می‌کند

اولین مورد از این قوانین تقریباً همه را در اولین باری که از یک برگه تولید شده محافظت می‌کنند، غافلگیر می‌کند. متد Protect را فراخوانی کنید و ناگهان هیچ‌کس نمی‌تواند در هیچ سلولی تایپ کند، از جمله ستون‌های ورودی که کتاب کار را بر اساس آن‌ها ساخته‌اید. هیچ چیز در کد شما آن ستون‌ها را لمس نکرده است، و این دقیقاً دلیل وقوع آن است

هر سلول قفل شده متولد می‌شود

استاندارد ECMA-376 ویژگی locked را به عنوان بخشی از رکورد قالب‌بندی سلول تعریف می‌کند و نه به عنوان ویژگی خود محافظت، و مقدار پیش‌فرض آن true است. محافظت از برگه صرفاً سوئیچی است که اجرای این پرچم را اجباری می‌سازد. بنابراین کل جدول از لحظه ایجاد، به صورت خفته حامل پرچم قفل است و فراخوانی متد Protect همه آن‌ها را به طور همزمان فعال می‌کند. راه‌حل این است که ترتیب را به طور عمدی تنظیم کنید: چیدمان را بسازید، محدوده‌هایی را که کاربران باید ویرایش کنند به طور صریح باز کنید و در نهایت محافظت را اعمال نمایید

Book := TXLSXWorkbook.Create;
try
  Sheet := Book.Sheets.Add('Timesheet');
  // ... سطر هدر، ستون نام و فرمول‌های نرخ در اینجا نوشته شده‌اند ...
  Sheet.Range['B2:B50'].SetLocked(False);         // پرسنل ساعت‌ها را در اینجا وارد می‌کنند
  Sheet.Range['F2:F50'].SetFormulaHidden(True);   // محاسبات نرخ را خصوصی نگه دارید
  Sheet.Protect('review-2026');                   // اکنون پرچم‌های قفل فعال می‌شوند
  Book.SaveAs('timesheet.xlsx');
finally
  Book.Free;
end;

متد SetFormulaHidden کار مجزایی را انجام می‌دهد که نادیده گرفتن آن آسان است: در حالی که محافظت فعال است، سلول همچنان مقدار محاسبه‌شده خود را نشان می‌دهد، اما نوار فرمول چیزی را نشان نمی‌دهد. این موضوع زمانی اهمیت دارد که یک فرمول حاوی نرخ‌های صورت‌حساب، حاشیه‌های سود یا وزن‌های امتیازی باشد که ترجیح می‌دهید به هر گیرنده‌ای که روی مجموع کلیک می‌کند نشان داده نشود. در XLS نیز همین هدف برای هر محدوده از طریق ویژگی‌های IXLSRange.Locked و FormulaHidden بیان می‌شود. برگه‌کار در آنجا همچنین دارای پانزده پرچم Allow* (مانند AllowSort، AllowAutoFilter، AllowFormatCells و غیره) است، بنابراین یک برگه محافظت‌شده همچنان می‌تواند مرتب‌سازی و فیلتر شود تا اینکه به یک سند کاملاً قفل‌شده تبدیل گردد

گذرواژه محافظت در واقع از چه چیزی محافظت می‌کند

هر دو فرمت گذرواژه محافظت از برگه و کتاب کار را به عنوان یک هش قدیمی 4 رقمی هگزادسیمال ذخیره می‌کنند. شانزده بیت به این معنی است که رشته‌های بی‌شماری با هر گذرواژه مشخص تداخل پیدا می‌کنند و ابزارهای حذف با یک جستجو در دسترس هستند. با محافظت به عنوان یک کمربند ایمنی در برابر ویرایش‌های تصادفی رفتار کنید و نه به عنوان کنترل دسترسی. این ابزار مناسبی برای جلوگیری از بازبینان از تایپ روی ستون فرمول است و ابزار اشتباهی برای هر چیزی است که با کلمه محرمانه ارتباط دارد

یک سطح بالاتر، متد ProtectWorkbook در XLSX ساختار کتاب کار را قفل می‌کند، که مانع از افزودن، تغییر نام، حذف یا تغییر ترتیب برگه‌ها می‌شود. هر زمان که لیست برگه‌ها خود پیمانی با یک تجزیه‌کننده پایین‌دستی است که برگه‌ها را بر اساس نام یا موقعیت اندیس‌گذاری می‌کند، آن را تنظیم کنید. تغییر نام یک برگه، وارد کردن داده در طرف دیگر را دقیقاً به اندازه حذف یک ستون خراب می‌کند. در XLS نیز همین لایه‌بندی با متد TXLSWorkbook.Protect در سطح کتاب کار و فراخوانی‌های Protect برای هر برگه، به همراه ویژگی isProtected برای کدی که نیاز دارد فایل به ارث رسیده را قبل از هرگونه اصلاح بررسی کند، منعکس شده است

هنگامی که نیاز به محرمانگی واقعی باشد، سازوکار کاملاً تغییر می‌کند. متد SaveAsEncrypted یک بسته رمزگذاری‌شده با AES را تحت طرح رمزگذاری استاندارد ECMA-376 تولید می‌کند که به طور دقیق در راهنمای خروجی XLSX محافظت‌شده با AES پوشش داده شده است، و در XLS نیز فایل‌های .xls رمزگذاری‌شده با RC4 از طریق ویژگی EncryptionPassword و سربارگذاری گذرواژه متد Open خوانده و نوشته می‌شوند. این تفاوت صرفاً تئوریک نیست. یک برگه محافظت‌شده به صورت متن ساده (cleartext) منتقل می‌شود، بنابراین هر ابزار فشرده‌سازی (zip) می‌تواند مقادیر سلول‌های آن را بخواند، در حالی که یک بسته رمزگذاری‌شده بدون گذرواژه غیرقابل خواندن است. یک بند حسابرسی که می‌گوید "فایل حقوق و دستمزد باید محافظت شود" تقریباً همیشه به معنای رمزگذاری است، از هر واژه‌ای که برای آن استفاده شود

تنظیمات صفحه بخشی از پیمان سند است

رفتار چاپ روی صفحه نامرئی است، به همین دلیل است که اغلب خراب منتشر می‌شود. لحظه‌ای که مشتری کتاب کار را چاپ می‌کند یا آن را برای حسابرس به PDF خروجی می‌گیرد، حاشیه‌ها، مقیاس‌گذاری و عناوین تکراری به الزامات عملکردی تبدیل می‌شوند که هیچ‌کس آن‌ها را آزمایش نکرده است. در XLSX این تنظیمات مستقیماً به برگه‌کار متصل هستند:

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';     // مرجع ساده: بدون نام برگه در اینجا
Sheet.PrintTitleRows := '$1:$1';     // سطر هدر در هر صفحه تکرار می‌شود
Sheet.FitToWidth := 1;
Sheet.FitToHeight := 0;              // با رشد داده‌ها به سمت پایین گسترش یابد
Sheet.PrintGridlines := False;

دو خط از آن کدها حاوی تله‌هایی هستند. رشته‌های هدر و فوتر از کدهای قالب‌بندی اکسل استفاده می‌کنند: &P برای صفحه فعلی، &N برای تعداد کل صفحات، به همراه کدهای &L، &C و &R برای آدرس‌دهی صریح سه بخش هدر. تله دیگر PrintArea است که عمداً یک مرجع ساده سلول را می‌پذیرد. HotXLS آن را بدون نام برگه ذخیره می‌کند و هنگام نوشتن فایل نام برگه را به عنوان پیشوند اضافه می‌نماید، بنابراین اگر خودتان 'Timesheet!$A$1:$F$60' را ارسال کنید، یک مرجع مخدوش تولید می‌شود. همین احتیاط یک لایه پایین‌تر نیز اعمال می‌شود: محدوده‌های چاپ و عناوین چاپ به عنوان نام‌های تعریف‌شده داخلی _xlnm.Print_Area و _xlnm.Print_Titles ذخیره می‌شوند، بنابراین هرگز ورودی‌های _xlnm.* را به طور دستی از طریق DefinedNames اضافه نکنید، وگرنه این دو مکانیسم روی یک اسلات با هم تداخل پیدا خواهند کرد

مقیاس‌گذاری که در برابر حجم داده‌های تولیدی دوام می‌آورد

ترکیب FitToWidth := 1 با FitToHeight := 0 به این صورت خوانده می‌شود: "همیشه ستون‌ها را در عرض یک صفحه جا بده، سپس به تعداد صفحات مورد نیاز برای داده‌ها به سمت پایین گسترش یاب"، و این حالت پیش‌فرض صحیح برای هر گزارشی است که تعداد سطرهای آن متغیر است. تله کار تنظیم یک درصد ثابت یا یک جفت تناسب با صفحه در برابر یک فایل آزمایشی سی سطری است: اگر همین تنظیمات را به ششصد سطر داده تولیدی بدهید، خروجی یا به ده‌ها صفحه بریده‌شده تبدیل می‌شود یا آن‌قدر کوچک می‌گردد که قابل خواندن نخواهد بود. عرض را مقیاس‌گذاری کنید، بگذارید طول رشد کند و سطر هدر را از طریق PrintTitleRows تکرار نمایید تا صفحه هفدهم همچنان به تنهایی قابل خواندن باشد

شکست‌های صفحه دستی نیز از همان قاعده بازتولید مانند هر چیز دیگری در کتاب کار تولید شده پیروی می‌کنند. متد AddRowBreak(BeforeRow) یک صفحه جدید را قبل از مرز یک بخش شروع می‌کند، اما زمانی که مولد دوباره اجرا می‌شود و سطرها تغییر می‌کنند، یک شکست قدیمی در وسط جدول قرار می‌گیرد. ابتدا ClearAllPageBreaks را فراخوانی کنید، سپس شکست‌ها را بر اساس شمارنده‌های سطری خود مولد دوباره محاسبه و اضافه نمایید به جای اینکه موقعیت‌های قدیمی را اصلاح کنید. در XLS کنترل‌های معادل در Sheet.PageSetup (جهت‌گیری، اندازه کاغذ، حاشیه‌ها، رشته‌های هدر و فوتر، تناسب صفحات)، به همراه RepeatRows و RepeatColumns برای پوشش عناوین چاپ قرار دارند

بررسی نتیجه قبل از اینکه مشتری این کار را انجام دهد

باگ‌های محافظت و چاپ یک ویژگی مشترک دارند: بررسی دستی آن‌ها بسیار ساده است اما تقریباً هیچ‌گاه بررسی می‌شوند. فایل تولید شده را در اکسل باز کنید و نود ثانیه برای آن وقت بگذارید. در یک سلول ورودی تایپ کنید و مطمئن شوید که ورودی را می‌پذیرد؛ در یک سلول قفل‌شده تایپ کنید و مطمئن شوید کادر محافظت ظاهر می‌شود؛ بررسی کنید که یک فرمول مخفی نوار فرمول را خالی می‌گذارد. سپس پیش‌نمایش چاپ (Print Preview) را در برابر یک مجموعه داده تولیدی بزرگ، و نه یک نمونه سی سطری، اجرا کنید و تعداد صفحات، سطر عنوان تکراری و شماره‌گذاری فوتر را بخوانید. پیش‌نمایش مرحله‌ای است که ارزش خود را ثابت می‌کند، زیرا هندسه چاپ به تنظیماتی بستگی دارد که روی صفحه رندر نمی‌شوند و بدون وجود یک چاپگر فیزیکی، پیش‌نمایش تنها جایی است که اشتباه مقیاس‌گذاری در آن نمایان می‌شود

یک تنظیم نهایی بررسی را کامل می‌کند. متد FreezePane(ACol, ARow) بلوک هدر را در حین اسکرول کردن کاربر در دید نگه می‌دارد. این یک رفتار روی صفحه است و نه رفتار چاپ، اما یک بازبین کل خروجی را به طور یکجا قضاوت می‌کند. و کتاب کاری که کار خود را به عنوان چیدمان نگهداری‌شده توسط یک طراح شروع می‌کند، بیشتر این‌ها را به طور رایگان دریافت می‌نماید: جریان کاری تولید گزارش از روی قالب تنظیمات صفحه را در قالب نگه می‌دارد، جایی که یک انسان آن را در برابر یک چاپگر واقعی تنظیم کرده است، و کار پر کردن داده‌ها و اعمال مجدد محافظت را پس از تثبیت چیدمان به کد واگذار می‌کند

HotXLS یک کتابخانه صفحه‌گسترده بومی Object Pascal برای دلفی و C++Builder است؛ مرجع کامل API محافظت و تنظیمات صفحه در صفحه محصول کامپوننت HotXLS قرار دارد