Bài viết kỹ thuật

Xuất một vùng ô Excel thành một ảnh duy nhất với HotXLS

Đôi khi sản phẩm bàn giao không phải một tài liệu, mà là một bức ảnh của một bảng. Một khối tóm tắt trong email trạng thái, một bảng KPI được kết xuất trong dashboard, một thumbnail cạnh kết quả tìm kiếm: tất cả đều muốn các ô và chẳng cái nào muốn giấy. TXLSCellImageExporter trong HotXLS nhận một hình chữ nhật ô kiểu cổ điển hay XLSX và tạo ra một PNG hay JPEG gọn gàng mà không có cỡ trang, lề, đầu trang hay chân trang, tiêu đề in, hay ngắt trang. Độ phân giải, tỉ lệ, định dạng và chất lượng JPEG cấu hình được, các đối tượng, đường lưới và viền ô có công tắc độc lập, nền có thể là một màu hoặc trong suốt, và việc ghi tệp đi qua một phép replace cùng thư mục nguyên tử giữ nguyên mục tiêu hiện có nếu có gì đó thất bại

Lý do thứ này cần một exporter riêng thay vì một cờ trên con đường in là phân trang không phải một tầng tùy chọn mà bạn có thể tắt. Nó chính là thứ mà pipeline trang tồn tại để làm

Vì sao không kết xuất vùng qua pipeline in?

Vì pipeline in chèn một trang giữa bạn và các ô. Cỡ giấy quyết định bao nhiêu vừa vặn, lề đẩy nội dung vào trong, đầu và chân trang chiếm các dải mà bạn không hề xin, tiêu đề in lặp lại các hàng mà bạn đã có, và ngắt trang xẻ đôi vùng. Một khối tóm tắt tình cờ vắt qua một điểm ngắt ra thành hai bức ảnh với hàng thú vị bị cắt làm đôi. Bạn có thể bù tất cả điều đó bằng cách dựng một cỡ trang tùy biến khớp chính xác vùng, và mọi người có làm vậy, nhưng điều đó nghĩa là tính lại hình học giấy mỗi khi vùng thay đổi và vẫn để dải đầu trang cùng logic tiêu đề in nằm trên đường

Exporter ô đo hình chữ nhật, cấp phát một bitmap đúng kích thước đó, vẽ các ô vào nó, và mã hóa. Chẳng có trang nào, nên chẳng có gì để cấu hình mà bỏ đi. Với các trường hợp bạn thực sự muốn giấy, con đường xuất PDF là công cụ đúng và được đề cập trong bài viết về xuất PDF bảng tính

TXLSCellImageExporter đo, vẽ và mã hóa một ảnh cho mỗi vùng ô trong khi pipeline in xẻ vùng tại các điểm ngắt trang
Pipeline trang chèn hình học giấy giữa bạn và các ô; exporter ô chẳng có trang nào ở bất kỳ đâu trên đường

Đo trước khi kết xuất

Measure trả về các kích thước pixel mà cài đặt hiện tại sẽ tạo ra mà không mã hóa gì. Điều đó quan trọng vì hai lý do. Một mẫu HTML hay email thường cần kích thước ảnh trước khi ảnh tồn tại, để dành chỗ hộp và tránh dịch chuyển bố cục. Và một dịch vụ kết xuất các vùng do người dùng chọn cần một cách để từ chối một yêu cầu phi lý trước khi cấp phát cho nó

uses
  lxHandleX, lxPagination;

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Exporter: TXLSCellImageExporter;
  Summary: TXLSXRange;
  W, H, Bytes: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('quarter.xlsx');
    Sheet := Book.Sheets.ByPos[0];
    Summary := Sheet.Range['A1:F20'];

    Exporter := TXLSCellImageExporter.Create;
    try
      Exporter.ImageFormat := xpifPng;   // PNG giữ các nét mảnh sắc nét
      Exporter.DPI := 96;
      Exporter.Scale := 2.0;             // đầu ra mật độ retina
      Exporter.IncludeGridlines := False;
      Exporter.IncludeCellBorders := True;
      Exporter.TransparentBackground := True;
      Exporter.MaxPixels := 40 * 1000 * 1000;
      Exporter.MaxBytes := 8 * 1024 * 1024;

      if not Exporter.Measure(Summary, W, H) then
        raise Exception.Create('range exceeds the configured budget');
      // W và H giờ đã biết; dành hộp bố cục trước khi mã hóa
      Bytes := Exporter.Save(Summary, 'summary.png');
      if Bytes <= 0 then
        raise Exception.Create('image export failed, previous file kept');
    finally
      Exporter.Free;
    end;
  finally
    Book.Free;
  end;
end;

Ngân sách, vì tỉ lệ nhân lên

MaxPixelsMaxBytes không phải trang trí phòng thủ. Số pixel tăng theo bình phương hệ số tỉ lệ và theo bình phương tỉ số độ phân giải, nên một vùng hợp lý 1200 nhân 800 ở 96 DPI trở thành xấp xỉ 47 megapixel ở 600 DPI, và một người dùng chọn cả vùng sử dụng thay vì một khối tóm tắt cộng thêm một bậc độ lớn nữa lên trên. Không có trần, dạng thất bại là một lần cấp phát mà tiến trình không thể đáp ứng, thứ kéo sập mọi thứ khác mà tiến trình đó đang làm

Có trần, yêu cầu thất bại và caller được lựa chọn: từ chối, giảm tỉ lệ, hay thu hẹp vùng. Đó là một vị trí tốt hơn nhiều cho một máy chủ báo cáo, và đó là cùng lý lẽ đằng sau các ngân sách tường minh trong bộ giải mã metafile được mô tả trong bài viết về bộ giải mã EMF và WMF có biên

Luồng ngân sách cho TXLSCellImageExporter trong HotXLS: Measure trả kích thước pixel trước, rồi MaxPixels và MaxBytes chặn cấp phát và kích thước đầu ra
Sự từ chối diễn ra trước khi cấp phát, và một thất bại ngân sách byte để nguyên ảnh trước đó cho caller

Phép replace nguyên tử, và vì sao thư mục quan trọng

Save vào một tên tệp không ghi vào mục tiêu. Nó ghi một tệp tạm trong cùng thư mục, mã hóa vào đó, và chỉ sau đó mới replace mục tiêu. Nếu mã hóa thất bại, nếu ngân sách bị vượt giữa chừng, hay nếu tiến trình bị giết, ảnh trước đó vẫn ở đó và vẫn hợp lệ. Một dashboard tái sinh các ô của nó theo lịch vì vậy chưa từng hiện một PNG bị cụt, triệu chứng quen thuộc của một phép ghi ngây thơ mở đích và bắt đầu chảy dữ liệu

Chi tiết cùng thư mục không phải ngẫu nhiên. Một replace nguyên tử chỉ nguyên tử trong một volume, vì giữa các volume hệ điều hành phải sao chép rồi xóa, điều tái giới thiệu đúng khung thời gian bạn đang cố đóng. Bất kỳ hiện thực nào của mẫu hình này đặt tệp tạm vào thư mục temp hệ thống đều không nguyên tử trên một máy mà đầu ra nằm trên một ổ khác

Save của TXLSCellImageExporter mã hóa vào một tệp tạm trong cùng thư mục rồi replace mục tiêu nguyên tử; thất bại giữ ảnh trước đó hợp lệ
Tệp tạm phải nằm cạnh mục tiêu vì một replace nguyên tử chỉ hoạt động trong một volume

Các sự kiện paint vẽ trên canvas thật

Cả exporter vùng lẫn exporter trang đều phơi các sự kiện paint đầu và đuôi, và chúng nhận một ngữ cảnh chỉ đọc đầy đủ thay vì chỉ một handle canvas. TXLSPagePaintContext mang canvas sống, các biên pixel, cỡ trang theo điểm, độ phân giải và tỉ lệ thực sự đang dùng, số trang tài liệu, số trang trong bảng, tổng số trang, tên bảng, và bảng tính nguồn gốc theo cả hương vị cổ điển lẫn XLSX. Đủ để vẽ một watermark co giãn đúng, hay một con dấu trang biết mình đang ở đâu trong lượt chạy

procedure TReportJob.StampDraft(Sender: TObject;
  const AContext: TXLSPagePaintContext);
begin
  // Nhận biết tỉ lệ, nên con dấu nhìn giống nhau ở 1x và 3x
  AContext.Canvas.Font.Height := Round(-48 * AContext.Scale);
  AContext.Canvas.Font.Color := clSilver;
  AContext.Canvas.Brush.Style := bsClear;
  AContext.Canvas.TextOut(AContext.Bounds.Left + Round(24 * AContext.Scale),
    AContext.Bounds.Top + Round(24 * AContext.Scale), 'DRAFT');
end;

Exporter.AfterPaint := Job.StampDraft;

Ba hành vi đáng để dựa vào. Các sự kiện bắn đúng một lần cho mỗi khung được kết xuất, gồm cả mỗi khung của một TIFF nhiều trang, nên một bộ đếm tăng trong handler đáng tin. Chúng giữ im lặng trong lúc đo, nên một handler có tác dụng phụ không chạy hai lần cho một đầu ra. Và nếu sự kiện đầu ném ngoại lệ, sự kiện đuôi không bắn và chẳng byte ảnh một phần nào được ghi, nên một ngoại lệ trong mã vẽ của bạn không thể tạo ra một tệp bị đóng dấu một nửa

Chọn định dạng

PNG cho mọi thứ giàu văn bản. JPEG áp dụng một phép biến đổi khối tạo ra tiếng vọng nhìn thấy được quanh các nét mảnh tương phản cao, và viền ô cùng văn bản nhỏ chính là những thứ đó, còn các lỗi nén sống sót ở những cài đặt chất lượng mà một bức ảnh trông hoàn hảo. JPEG giành được chỗ của mình khi vùng bị chi phối bởi các ảnh nhúng và kích thước tệp quan trọng hơn độ trung thực cạnh. Nền trong suốt đòi PNG, vì JPEG không có kênh alpha, nên một ô định nằm trên một bề mặt có màu đã chọn thay bạn

Nếu vùng của bạn chứa các ô gộp, hãy kiểm đầu ra đối chiếu với bảng: các vùng gộp tương tác với bề rộng cột theo những cách khiến người ta ngạc nhiên, và các quy tắc bố cục được đề cập trong bài viết về ô gộp và mẫu báo cáo. HotXLS đọc và ghi XLS, XLSX, ODS và CSV từ Delphi và C++Builder mà không phụ thuộc Excel, và toàn bộ bề mặt exporter được ghi tài liệu trên trang sản phẩm HotXLS Delphi spreadsheet component