Một sổ làm việc Excel có thể mang các ảnh EMF và WMF, và cách thông thường để vẽ một cái là giao luồng byte cho trình phát metafile của hệ điều hành. Đó là một quyết định đáng được nhìn thẳng: một metafile là một luồng lệnh đã tuần tự hóa cho một API đồ họa, và phát lại nó nghĩa là để một tệp đến qua email lái driver đồ họa. HotXLS chọn con đường khác. XLSDecodeVectorScene tự phân tích metafile, xác thực header, từng kích thước bản ghi, tổng số bản ghi được tuyên bố và vị trí chính xác của bản ghi kết thúc tệp, từ chối thẳng các bản ghi escape, và trả về một TXLSVectorScene gồm các lệnh vẽ nguyên thủy mà các backend Canvas và SVG phát lại qua mã của riêng chúng. Không có playback driver nào dính dáng ở bất kỳ điểm nào
Cuộc đánh đổi là độ phủ lấy tính kiềm chế. Một whitelist lệnh hướng hình chữ nhật sẽ không tái hiện được mọi metafile mà một nhà thiết kế có thể tạo, nên scene báo có bao nhiêu bản ghi vẽ mà nó không thể biểu diễn và caller quyết định làm gì với điều đó. Với một tiến trình máy chủ kết xuất những tài liệu mà nó không tạo ra, cuộc đánh đổi đó là chiều đúng
Vì sao playback metafile là lựa chọn kém cho đầu vào không tin cậy?
Vì định dạng đó không phải một bức ảnh, mà là một chương trình. Một luồng bản ghi EMF thao túng một ngăn xếp trạng thái device-context, cấp phát và chọn đối tượng từ một bảng handle, và có thể mang các bản ghi escape mà phần dữ liệu được chuyển thẳng tới một driver thiết bị. Phát lại nó tác động các đường trong ngăn xếp đồ họa nền tảng được viết với giả định metafile đến từ một ứng dụng hợp tác trên cùng máy. Khi đầu vào là một tệp đính kèm bảng tính, giả định đó biến mất, và bao nhiêu cẩn thận bên trong thư viện bảng tính cũng vô ích vì thư viện không phải thành phần làm việc phân tích
Đây là cùng lý lẽ chi phối tầng container. Một sổ làm việc là một kho ZIP, và HotXLS xác thực central directory của nó thay vì tin các offset được tuyên bố, như được mô tả trong bài viết về xác thực ZIP end-of-central-directory. Các payload metafile là tầng kế tiếp của cùng vấn đề
Bộ giải mã kiểm tra gì trước khi vẽ bất cứ thứ gì
Phép xác thực mang tính cấu trúc và diễn ra ngay từ đầu, vì một parser bắt đầu vẽ và xác thực trong lúc đi đã hành động trên dữ liệu mà nó chưa xác minh. Header phải khớp nghiêm ngặt thay vì có vẻ khớp. Mỗi bản ghi phải tuyên bố một kích thước nằm gọn trong phần buffer còn lại và đủ lớn cho các trường cố định của chính nó. Số bản ghi mà header tuyên bố phải khớp với các bản ghi thực sự hiện diện. Bản ghi kết thúc tệp phải nằm đúng nơi luồng kết thúc, chứ không chỉ gần đó, điều chặn đứng mẹo rác-đuôi giấu một payload thứ hai sau một bức ảnh hợp lệ
Ngoài cấu trúc, bộ giải mã fail-closed về ngữ nghĩa. Các bản ghi escape bị từ chối, không bị bỏ qua. Một bản ghi đổi trạng thái mà bộ giải mã không mô hình hóa khiến việc giải mã thất bại thay vì bị lờ đi, vì lờ một thay đổi trạng thái nghĩa là mọi lệnh vẽ sau đó được thực thi trong một trạng thái mà tệp không hề yêu cầu, và kết quả là một bức ảnh sai theo cách không ai dự đoán được. Các bản ghi vẽ ngoài tập lệnh được hỗ trợ là chuyện khác: chúng được đếm và bỏ qua, vì một hình dạng còn thiếu là một khoảng trống nhìn thấy được, báo cáo được chứ không phải hư hỏng câm lặng
Ngân sách là một phần của hợp đồng định dạng
Các định dạng vector có phiên bản riêng của bom giải nén. Vài kilobyte bản ghi có thể tuyên bố các polyline với hàng trăm triệu điểm, hay một ảnh mà các kích thước được tuyên bố nhân lên thành terabyte. Vì vậy các biên phải là các hằng số tường minh thay vì mức mà máy tình cờ chịu đựng được
// Từ lxVectorScene: ngân sách giải mã, được tuyên bố chứ không ngầm định
XL_VECTOR_MAX_RECORDS = 1000000;
XL_VECTOR_MAX_HANDLES = 4096;
XL_VECTOR_MAX_DC_DEPTH = 32;
XL_VECTOR_MAX_COMMANDS = 100000;
XL_VECTOR_MAX_POINTS_PER_RECORD = 100000;
XL_VECTOR_MAX_TOTAL_POINTS = 2000000;
XL_VECTOR_MAX_TEXT_CHARS = 4096;
XL_VECTOR_MAX_TOTAL_TEXT_CHARS = 1000000;
XL_VECTOR_MAX_IMAGE_SIDE = 8192;
XL_VECTOR_MAX_IMAGE_PIXELS = 32 * 1024 * 1024;
XL_VECTOR_MAX_IMAGE_BYTES = 64 * 1024 * 1024;
XL_VECTOR_MAX_COORD = 1000000000;
Hai trong số này đáng một lời ghi. Trần độ sâu device-context bằng 32 tồn tại vì các bản ghi SaveDC và RestoreDC lồng nhau, và một luồng lệch cân bằng có thể đẩy mãi mãi; 32 là hào phóng cho metafile thật và rẻ để thi hành. Trần tọa độ tồn tại vì tọa độ nạp vào một phép biến đổi, và một giá trị gần giới hạn khoảng số nguyên tạo ra một kết quả biến đổi vô cùng hoặc tràn, sau đó mọi phép tính bounding-box ở hạ nguồn đều vô nghĩa. Kẹp tọa độ lúc phân tích dễ suy luận hơn nhiều so với phòng thủ từng người tiêu thụ hình học
Dùng scene
Bộ giải mã trả lại một đối tượng bạn sở hữu, một số lệnh, một kích thước danh nghĩa, và một số bản ghi vẽ mà nó chọn không biểu diễn
uses
lxVectorScene;
var
Scene: TXLSVectorScene;
Error: WideString;
I: Integer;
begin
// Data giữ payload ảnh thô lấy từ sổ làm việc
if not XLSDecodeVectorScene(Data, xlsvfEmf, Scene, Error) then
begin
// Bị từ chối: header, biên, tổng số, vị trí EOF hoặc một ngân sách
LogReject('metafile rejected: ' + Error);
Exit;
end;
try
if Scene.SkippedDrawRecords > 0 then
LogWarning(Format('%d drawing records outside the safe subset',
[Scene.SkippedDrawRecords]));
for I := 0 to Scene.Count - 1 do
case Scene.Commands[I].Kind of
xlsvcRectangle: DrawRect(Scene.Commands[I]);
xlsvcEllipse: DrawEllipse(Scene.Commands[I]);
xlsvcPolyline,
xlsvcPolygon,
xlsvcBezier: DrawPath(Scene.Commands[I]);
xlsvcText: DrawText(Scene.Commands[I]);
xlsvcImage: DrawImage(Scene.Commands[I]);
end;
finally
Scene.Free;
end;
end;
Bản ghi lệnh mang mọi thứ một backend cần và chẳng gì đòi hỏi thiết bị: hiện diện bút, màu, bề rộng và kiểu; hiện diện cọ và màu; hình học; và với văn bản là chuỗi, tên phông, cỡ, các kiểu và căn lề. Đó là điều khiến cùng một scene dùng được bởi cả bộ kết xuất canvas trên màn hình lẫn bộ viết SVG, và đó là lý do con đường vector không phân kỳ giữa bản xem trước và bản xuất. Kết xuất màn hình nội dung bảng tính nói chung được đề cập trong bài viết về kết xuất lưới VCL tùy biến
Từ chối một bức ảnh không làm hỏng sổ làm việc
Một thuộc tính quan trọng của thiết kế này là một lần giải mã bị từ chối chỉ ảnh hưởng kết xuất. Payload gốc vẫn ở trong mô hình, nên một sổ làm việc được mở và lưu lại sẽ mang các ảnh metafile của nó ra nguyên từng byte một, bất kể bộ giải mã an toàn có vẽ được chúng hay không. Con đường raster có biên hiện có cũng vẫn sẵn sàng như một phương án dự phòng. Nói cách khác, parser nghiêm ngặt cổng cái được thực thi, không phải cái được bảo quản, và đó là sự phân biệt cho phép một thay đổi vì mục đích an ninh được phát hành mà không biến thành một thay đổi mất dữ liệu
Xử lý đối tượng vẽ nói chung, gồm các phần của mô hình đối tượng sống sót qua các vòng trọn không sứt mẻ, được đề cập trong bài viết về biểu đồ, ảnh và bản vẽ
Điều này đặt một bản triển khai máy chủ vào đâu
Nếu bạn kết xuất các sổ làm việc do người dùng tải lên trong một dịch vụ, lập trường thực tiễn giờ bảo vệ được: các ảnh metafile được phân tích bởi mã bạn có thể kiểm toán, bị chặn bởi các hằng số bạn có thể đọc, và chưa bao giờ được giao cho một driver đồ họa. Câu thắn là độ phủ. Các metafile phức tạp do công cụ vẽ tạo ra sẽ đụng bộ đếm bản ghi bị bỏ qua, và câu trả lời là phơi bộ đếm đó ra thay vì lặng lẽ nới whitelist. Một bức ảnh kết xuất một phần và nói vậy là một cuộc trao đổi hỗ trợ; một bức ảnh kết xuất sai và im lặng là một báo bug từ khách hàng
HotXLS xử lý XLS, XLSX, ODS và CSV một cách thuần túy trong Delphi và C++Builder mà không cần cài Excel, và triết lý phân tích có biên chạy xuyên suốt các tầng container, công thức và bản vẽ của nó. Chi tiết định dạng và an ninh được liệt kê trên trang sản phẩm HotXLS Delphi spreadsheet component