Bài viết kỹ thuật

Kết xuất Bảng Dữ liệu ra PDF trong Delphi với HotPDF

Một tập dữ liệu (dataset) vốn là những hàng với cột ngang dọc; còn trang PDF lại rỗng tuếch như một mặt phẳng chia tọa độ chả thèm đoái hoài gì đến khái niệm đó. Lấp đầy khoảng cách khác bọt đấy là trọng tâm toàn bộ bài toán nơi đây. Đào mỏi tay cũng chả ra cái lệnh DrawTable nào trong HotPDF chịu xơi nguyên cục dataset để rồi phọt ra sẵn mâm lưới kẻ chia lề ngay ngắn đâu. Cái bạn được bón cho thực chất là đống vật liệu thô để nặn thành lưới: TextOut thả chữ tại điểm định sẵn, SetFont đẽo mặt mũi kiểu chữ, Rectangle với Fill rải màu nền dải ngang, và nhóm MoveTo / LineTo / Stroke quất nét kẻ đường. Chế tạo một thợ săn xuất PDF dạng bảng thành công chính là quy củ biến mớ ý tưởng hàng-và-cột trừu tượng ra thành những cặp tọa độ x và y trần trụi, và rồi phải túm gáy giữ đống tọa độ đó đi theo khuôn khổ lúc lượng data tuôn tràn lọt rốn qua khỏi mép đáy của tờ giấy

Kịch bản bưng mẫu bên dưới báo cáo mớ hồ sơ khách hàng, cơ mà cái đống code đảm nhận chọc cọ vẽ chả thèm đếm xỉa lỡ đống dòng dữ liệu đó mọc ra từ lỗ nẻ nào. Cái bản nháp khởi đầu xài đồ cổ TTable; vứt vô 1 query FireDAC, mớ dataset bung trong rốn in-memory, hay 1 mảng (array) trần trụi mớ records cũng vẫn đút vào ngốn ráo y chang cái sườn code nguyên si không mẻ mảy may. Mấu chốt ở đây là bạn có thể rảo bước cày qua đống data từng dòng một rồi chọc móc đục lấy bốn cái trường (field) chuỗi rặn ra từ mỗi cục đó. Cứ chia cắt rạch ròi phân mảng phần kết xuất (rendering) khỏi cái vòi phun dữ liệu (data source) là vác mặt đi bẻ lái đổi ngả nào tùy ý chả ngại quậy vỡ mâm bên kia

Đo đẽo hình học cột kèo đi tiên phong

Trước lúc rải xuống bất kỳ nét con chữ nào, chốt sổ liền mấy cái cột an tọa nằm đâu cái đã. Một cái bảng mọc ra ở đây mang trong bụng bốn cột, suy ra ẻm đòi liền bốn cái mép trái và biên phải rành rành. Đóng cứng con số cụ thể thẳng tuột tại ngay miệng mấy cú hú gọi TextOut, cái trò mấy cái ví dụ cắn xén tóm lược khoái xài, đích thị là cái ung nhọt dọn đường táng vỡ mồm sau này khi muốn nới cột mập ra. Cứ gọi đích danh mấy mép lề đó một đợt duy nhất, đo ni vạch bằng point soi từ gốc dưới tay trái mọc lên, rốt cục thì mọi cú hú lệnh rải vẽ sẽ dõng dạc xướng tên chúng nó ra:

const
  ColNo   = 70;    // left edge of the "No." column
  ColName = 110;   // company name
  ColAddr = 300;   // street address
  ColCity = 480;   // city
  RowLeft = 50;    // table frame: left rule
  RowRight = 570;  // table frame: right rule
  RowStep = 20;    // vertical distance between baselines

procedure PrintRow(Page: THPDFPage; Y: Single;
  const ANo, AName, AAddr, ACity: string; Shaded: boolean);
begin
  if Shaded then
  begin
    // A shaded band behind the row. Rectangle takes X, Y, Width, Height.
    Page.SetRGBFillColor($00FFF3DD);
    Page.Rectangle(RowLeft, Y - 4, RowRight - RowLeft, RowStep);
    Page.Fill;
    Page.SetRGBFillColor(clBlack);
  end;
  Page.TextOut(ColNo,   Y, 0, ANo);
  Page.TextOut(ColName, Y, 0, AName);
  Page.TextOut(ColAddr, Y, 0, AAddr);
  Page.TextOut(ColCity, Y, 0, ACity);
end;

Hai mẩu tình tiết nhỏ vớt vát lại giá trị ăn tiền ở đây. Miếng dải tô màu nền rải trước cái đã, chữ đè bẹp lên sau, cái lệ đó do lề lối trát màu tô trong PDF chơi theo xếp lớp trục z (z-order): lỡ tay lót màu đổ fill cái hình chữ nhật sau khi nhả đám text thì xác định đám text kia chầu trời dưới mồ. Với lại cái màn đổ bóng tô màu lốm đốm cách dòng đó chả sinh ra đặng múa may cho vui sướng. Quăng mắt vào một đống báo cáo nhung nhúc đặc chữ thì chiêu đó là kèo thơm rẻ thúi nhất đỡ ánh nhìn trượt lầm trật đường rầy nhầm dòng khác, đấy là nguyên do cái vòng lặp phía tuốt ở dưới lật ngửa cái boolean liên tục theo từng dòng và quẳng nó vô thẳng họng của biến Shaded

Tọa độ của những cây cột nằm bẹp lù lù bên trên bị neo cứng ngắc, như thế là quá thật thà cho một bản report bạn nắm thóp từ a tới á cái sườn sơ đồ. Gặp đống dữ liệu trồi sụt phình lép linh tinh, vác thước đo ra đo đi đừng nặn óc đoán mò. HotPDF mở toang cửa mâm đo đạc độ rộng chữ mọc sẵn trên cụm đối tượng page, vậy chi nhánh đẻ con PrintRow phiên bản production đủ sức bú mớm lấy đoạn text bự dài loằng ngoằng nhất của từng cột, lôi ra xào đo một bận rớt theo số size chữ chọn mặt gửi vàng, rồi đẻ ra mớ lề mép trái từ mớ chiều rộng đo phễu đó bồi thêm khoảng hở vách nữa. Cốt lõi cái dáng hình thủ tục kia y chang phốc chả xi nhê gì; chỉ mỗi cái xuất thân ruột gan của cái mâm hằng số bị hoán xác mà thôi

Cái header, mớ đường kẻ, và góc độc quyền bao thầu

Bảng kéo dài lết tuột khỏi mặt trang và chòi ngóc qua trang nối tiếp mà cụt ngủn chả có dán nhãn tên đầu cột là mớ phế liệu đọc đếch ra hồn. Chốt sổ phương án gỡ rối nằm ở việc coi cái khối header đó như thể thứ bạn nặn ra quết lại vẽ thêm lần nữa chứ không phải nặn một bận là thôi. Lùa nhét cái đống title đầu cột cùng đống thước ngang đẽo thành viền rào nhét vào chung một cục thủ tục (routine) rọi duy nhất, và đâm chọc móc lôi nó ra nổ cả lúc xuất phát ban đầu lẫn mỗi bận bạn toạc giấy sang xé thêm trang mới. Nhờ đặc điểm header nặn chung với lòng thân body ở kho hằng số các cột, cả đám ăn rơ thẳng thớm y một luống tự nhiên

procedure DrawHeader(Page: THPDFPage; var Y: Single; PageNo: Integer);
begin
  // Left: source label and page number. Right: generation time.
  Page.SetFont('Arial', [fsItalic], 10);
  Page.TextOut(RowLeft, Y, 0, 'customer.db   Page ' + IntToStr(PageNo));
  Page.TextOut(ColCity, Y, 0, DateTimeToStr(Now));

  // Two horizontal rules that box the column titles.
  Page.MoveTo(RowLeft, Y + 15);
  Page.LineTo(RowRight, Y + 15);
  Page.MoveTo(RowLeft, Y + 45);
  Page.LineTo(RowRight, Y + 45);
  Page.Stroke;

  // The column titles, in a heavier face so they read as headings.
  Page.SetFont('Times New Roman', [fsBold], 12);
  Page.SetRGBFillColor(clNavy);
  PrintRow(Page, Y + 25, 'No.', 'Company', 'Address', 'City', False);
  Page.SetRGBFillColor(clBlack);

  Y := Y + RowStep + 45;  // advance past the boxed header before the first body row
end;

Dòm cho kỹ cái cục DrawHeader ôm múc biến Y xài tham chiếu (by reference) xong đùn đẩy sấn nó bước tới coi. Phía gã gọi (caller) khỏi tốn tế bào não moi nhớ cái mớ header nó mập lù lù cao cỡ bao lăm; bản thân cái cục routine trét họa nó chính là gã rõ rành điều đấy nhất. Luật lệ nắm trịch mâm quyền sở hữu đó là hòn đá tảng ghì gánh bợ bố cục khỏi lết trượt lênh đênh lỡ chăng sau này bạn nhồi tọng cái logo hay nện bản tóm gọn bộ lọc (filter summary) vào cõi ranh header. Cái vòng lặp băm body vẫn cứ ngu ngơ chả mảy may xi nhê. Nó vẫn đâm nhát cuốc nặn đẽo dòng đều đặn từ rốn xuất phát bất kể tọa độ Y chĩa mùi vô chỗ khỉ ho cò gáy nào

Lên khung đống đường kẻ viền ranh đó đích thị là nhát chém gọt nên sự phân tranh khác bọt giữa mớ liệt kê dạng list với một cục bảng table oách xà lằng. Mấy ranh ngăn cột xổ dọc bản chất ốp xào lại y chang luồng tư duy dội lên trục x: nện MoveTo / LineTo / Stroke ngậm ngay mép rìa mỗi cột, lùa cọ kẻ chải thẳng từ thước ngang nắp mâm lút xuống vạt vạch chót đáy rốn dòng ranh giới bét nhen trang đó. Bản sample này ôm giữ mấy dòng ngang cốt hòng lột vạt cho mướt mắt đọc thôi, chứ cái bước cày bừa bản production đùn ra đống máy móc dập khuôn rột rẹt một khi rổ hằng số ôm mớ tọa độ đã phơi mình thâu tóm

Cái vòng lặp con trỏ chầu hẫu bao vụ nhát cắt trang

Khoản phết vẽ nặn nhào chỉ là mẩu bèo vặt. Cái vế nhằn lòi mắt vạch chia gianh một cục hình thù vớ vẩn khác bọt với mâm report hàng auth chính là nhát dao đẽo phân trang (pagination): tường tận thấu đáo rành mạch, ngay trước khi quệt cọ thả nét xuống, đong đếm ướm thử ẻm có nhét bóp lọt khuôn hay chăng, và dội nổ lôi bung giấy tinh khôi lòi kèm chòm nón header lốp bốp một khi đụt bít tắc. Quyền chốt hạ đấy cắm neo dính tại đúng một rốn duy nhất, chính chóc cái vòng lặp lội nhào xới cuốc lùa đám dữ liệu ấy, chứ chả phải ở lỗ nẻ nào khác sất

var
  Pdf: THotPDF;
  Page: THPDFPage;
  Y: Single;
  PageNo: Integer;
  Shaded: boolean;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'CustomerReport.pdf';
    Pdf.BeginDoc;
    Page := Pdf.CurrentPage;

    // Report title, once, at the top of the first page.
    Page.SetFont('Arial', [fsBold], 24);
    Page.TextOut(200, 800, 0, 'Customer Report');

    PageNo := 1;
    Y := 760;
    DrawHeader(Page, Y, PageNo);
    Shaded := False;

    CustomerTable.First;
    while not CustomerTable.Eof do
    begin
      // Out of room? Open a new page and repeat the header there.
      if Y < 60 then
      begin
        Pdf.AddPage;
        Page := Pdf.CurrentPage;   // AddPage moves CurrentPage forward
        Inc(PageNo);
        Y := 760;
        DrawHeader(Page, Y, PageNo);
      end;

      Shaded := not Shaded;
      Page.SetFont('Arial', [], 10);   // SetFont must be reissued on every new page
      PrintRow(Page, Y,
        VarToStr(CustomerTable['CustNo']),
        VarToStr(CustomerTable['Company']),
        VarToStr(CustomerTable['Addr1']),
        VarToStr(CustomerTable['City']),
        Shaded);

      Y := Y - RowStep;
      CustomerTable.Next;
    end;

    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Cặp dữ kiện la bàn cắm tọa độ lùa dồn tổng quản nguyên ổ vòng lặp. PDF đo ni tấc đẽo y tót chĩa ngược lên rốn mọc từ nách góc trái dưới đáy, thành thử lốc lô dòng cuốc bước dấn xuống lút đít trang rập bằng rập đục bớt khoét khoán RowStep lẹm lút khỏi Y nhích nháy mỗi cữ, còn cái bài test soi đầy tràn nổ cò dội cái đụp chóc lọt thời khắc Y trượt thọt đuông tuột qua bờ vực lề bẹn đáy chứ đâu có ngóc 1 phát vắt đỉnh trần nhà nào. Nhảy lố sai cái ngả hướng đấy phát rụng luôn dải dòng đầu lọt lõm trôi bạt vào miền quên lãng tuột nách bờ dưới trong khi nguyên cái vòng lặp vẫn mộng du lảm nhảm bụng nó trống trải mơn mởn mâm trang giấy bành trướng

Một điểm khứa họng thứ hai chực bóp cò chộp cổ bẫy giò ai xui một nháy đui mù. AddPage hú đẻ sấn trang mới lanh tanh rồi lèo lái thụt nòng dời rốn CurrentPage neo đậu lên tọt mâm mới đó, ác đạn cái nó móc tuột luốt chả bê ẵm theo thứ bèo bọt nào: phông font chùi tịt, bảng màu sơn bôi trét rỗng toác, bệ rốn định vị trốc rễ. Cớ sự đó lôi tuột lý do tại vì sao mà Page phải ngọ nguậy lôi ra bới đọc cạ lại mâm CurrentPage sau đít mỗi gót bồi AddPage, và trần ai vớt cú SetFont đâm mồi dội lên hú mòi chầu lặp phát chót gõ đầu đống mớ hàng body. Cúp cua bùng phéng phát nạp mớ đọc lại kia là coi như cọ sơn vẫn bôi mướt mải bẹp dính lên cái trang bạn vừa vứt xó ển quẳng đi đó; búng bỏ ngúng ngẩy vụ nạp phông font đâm ra bản nặn lôi lên hiển thị theo bất chấp cái của nợ hình hài quái dị nào dội ngược từ mâm mặc định đám trình chiếu viewer vứt trả vô mặt

Các quả tai nạn đứt gánh giữa đường hạ bệ thợ xuất Table

Cả xớ lố bọ lù lù lôi cổ sập gầm bảng table đa phần ếm ngải ẩn thân chả thèm ỉ ôi nhảy ồ vạch mặt lúc bạn đi mượt lết cày chục dòng rập mẫu đẽo ngoan ngoãn. Nó chui hang ở tận mấy cái rìa lề mép cắt, và mấy đường gờ đó thì chả mất 1 cắc để lôi ra rạch mặt bắt nạt chừng bạn đã mường tượng ra ổ núp của chúng

  • Tập datasets trống rỗng toạc rốn. Bụng nhồi đập mâm vắt vẻo cuốc số 0 rụng ra tờ giấy đẻ lòi nón header cọc lốc chả vắt sữa ướt bụng dòng nào sất, dù sao thì cũng ngó dòm trông có vẻ hệt dáng điệu tự chủ gài mắm dậm muối cố tình vạch ra thế. Tờ 100% bóc trắng trơn bóng lưỡng chả lòi một nhúm cọng lông header nào thì đanh lòi vẻ táng banh đổ bể tè le. Phân xử quyết liệu xem chuộng thể loại gì vác lên mâm trước khi thả đóng gạch vớt nòng vác xuất quân
  • Cái dòng rớt mụ chóc cắm lọt cái lằn ranh. Rặn nổ một bộ báo cáo cõng dính dải dòng chót tọt tọa kề mấp mé chênh lên nửa nấc bên trên bờ lề mâm, rồi dội vớt phát kế tiếp thọt trúng vệt dòng sau đút tuột xuống tọt 1 nấc lút đít dưới. Cú lạng xích vấp lề hụt 1 trang đâm giấu trĩ kĩ đến độ trừ phi dữ liệu khớp vừa vặn 100% chóc với độ cong điếm thúi vặn vẹo khập khiễng rành rành sai trật lất
  • Lố vạch mảng chữ ứ hự tràn viền. Đẻ cái tên công ty phình lố đè bè bè ngậm mồm rúc béo ú đè sấn lên vách ranh cột tiếp theo. Gạch mâm thước nắn gọt đo độ chóp và đóng đinh mâm luật lệ trị nó nghen: bẻ vặn nắn bọc gãy lọt dòng bồi thứ 2, nhát kéo xén cụt, hoặc băm lóng ngắt bằng chùm dấu chấm lửng. Câm nín là ngọng chứ chả phải chính sách trị nước
  • Nhúm lố trường rỗng Null. Chọc nạp thẳng tọt cọng Null nạp tuột qua ống tiêm TextOut coi chừng ói chọc ngó ra bản chữ thô xù xì lềnh phềnh Null chễm chệ chình ình, hoặc giả là cục trắng hếu trống lơ trống lốc, tùy duyên bạn cày đẽo luộc nó bằng bùa gì. Kén lửa lật nướng render mảng này cho tử tế thay vì buông thả để cho mảng nhồi nhét ép uốn variant tự nặn lựu đạn chọi ngược bạn

Lùa con bò kết quả đút phang trượt test chọt qua sấn chục cái cối xay Viewer ngó chừng trước lúc tự nổ cờ phất tay bế mạc kết game. Trò thay font đắp chữ với trò xén gọt cành cộc (clipping) nó bung quẩy 360 độ lộn mèo biến tướng dị hợm lắm ngang nách các mâm xay kết xuất renderer, chả trách cái bảng ngó dòm đoan trang chữ điền đẽo vát trong lọt cái mắt này thì nổ bung chành rách hàng hoặc đục khoét cột phố trật lất ở bển kia. Test lật mâm dò chắc chót cú nổ nón header cõng trên chóp lặp tự động nọ, bộ da nện khối đổ bóng sọc dưa, với mâm mép ranh vẫn xẻ tà nguyên đai lót mạng trong khúc trung chuyển lướt sóng mới, và chuỗi nấc số trang khắng khít liền tù tì khi con sóng dữ liệu lột vách thọt lặn ngụp nhảy phốc rào

Cặm cụi tự phác thảo nhát kẻ cái lưới xào tay thay vì đu bám cái máy dập lôi đống báo cáo lòe loẹt trực quan thì nhọc óc ăn mớ mã lệnh nhiều vãi, đổi lại mâm quả ngọt đớp miếng bù xớt trọn vẹn đáng xẻ phanh nếm cho bằng được: bạn nắm trọn uy quyền kiểm soát bóp mọi thông số tọa độ, cái đó sinh ngỏ đúng chóc ý đồ trị mấy trận bão chạy cối xay sấp mặt bên bờ cụm server mịt mù, mấy chồng hóa đơn dày cộp vất kho, rồi mớ rải xuất audit đổ đống đòi hỏi nặn phọt mâm y chang hình bóng trên cả rổ thập cẩm thiết bị bạt ngàn, và nó triệt trọn cọp chi phí độn giá lềnh kềnh rườm rà bạn luôn lăm le bóp mũi đạp chóp cho rụng tọt đi vớt lấy nhúm ruột listing rẻ bèo húp trộm. Nặn chóc ở nấc đầu, sức mạnh tay to nhào nắn đẻ đủ lãi rớt lố cho chính nó mâm đó hễ cứ chớm có kèo nổ còi một bản báo cáo phải hạch sách soi rọi chuẩn đét ngoài bãi nổ chiến trường trơn tuột y hệt dòm trên đùi bàn của bạn

Chùm luật lệ mớ đường kẻ và các viền dải vạt bóng che kia dựa dẫm lưng vào chùm nguyên thủy màu trét lố vector móc nối cặn kẽ giải phẫu trong chuyên mục lôi cọ dạo chơi canvas vẽ vời, nếu như bạn khát nếm đớp mâm bồi thêm về đống cọc Rectangle, MoveTo, rồi cả bộ kéo bè LineTo hòng dằn dơ trị riêng lẻ nó trước tiên đong đầy dạ. Lố dập đồ nghề vặt múa họa dùng chóc ở vách hẻm này quy tụ bâu làm một cọc của 1 sườn cõng con dâu Thành phần (Component) HotPDF trị lụt cho rốn Delphi kèm ẵm ẵm C++Builder