HotPDF xuất một trang của bất kỳ tài liệu PDF đã tải nào thành mã đánh dấu SVG độc lập chỉ bằng một lệnh gọi duy nhất, BuildLoadedPageSVG, hàm này trả về toàn bộ tài liệu SVG dưới dạng chuỗi. Mã đánh dấu xuất ra mang theo hình học của trang, văn bản dưới dạng phần tử text SVG thực sự, hình ảnh raster nhúng, và trạng thái nét vẽ mà các toán tử PDF đã thiết lập tại từng thao tác vẽ
Phần cuối cùng đó chính là nơi phần lớn bộ chuyển đổi tự viết âm thầm sụp đổ. Biến một trang PDF thành SVG trông như bài toán tọa độ nhưng hóa ra lại là bài toán trạng thái. PDF là một máy ngăn xếp có trạng thái đồ họa thay đổi khi luồng nội dung được diễn giải; SVG là một cây khai báo mà mỗi phần tử mang thuộc tính trình bày riêng của nó. Bất cứ điều gì mà bộ diễn giải không chụp lại đúng thời điểm một phần tử được phát ra sẽ đơn giản biến mất khỏi đầu ra, và lỗi này âm thầm không báo: bạn nhận được SVG hợp lệ nhưng render ra một trang sai lệch tinh vi
Vì sao trang PDF không thể chuyển đổi trực tiếp sang SVG?
Ba điểm không tương thích khiến việc chuyển đổi không hề đơn giản, và cả ba đều tạo ra đầu ra trông có vẻ hợp lý cho đến khi bạn đặt cạnh nhau so sánh với bản gốc. Điểm đầu tiên là trục y. Không gian người dùng của PDF tăng dần lên trên từ góc dưới-trái của trang; SVG tăng dần xuống dưới từ góc trên-trái. Một phép lật ở cấp trang sửa đúng tọa độ vẽ nhưng lại làm hỏng mọi glyph, vì lật toàn bộ canvas cũng lật gương luôn cả hình dạng chữ cái
Điểm không tương thích thứ hai là tính kế thừa. Trong PDF, q và Q đẩy vào và lấy ra khỏi ngăn xếp một trạng thái đồ họa bao gồm độ rộng nét, kiểu đầu nét, kiểu nối nét, giới hạn miter, mảng nét đứt, pha nét đứt và độ trong suốt. Trong SVG, một phần tử không khai báo thuộc tính sẽ kế thừa thuộc tính đó từ nhóm tổ tiên, đây là quy tắc phạm vi hoàn toàn khác. Một bộ xuất chỉ theo dõi ma trận biến đổi hiện tại mà quên mất trạng thái nét vẽ sẽ để trạng thái được khôi phục sau một Q rò rỉ sang các phần tử phía sau
Điểm thứ ba là PDF biểu diễn một số thứ bằng quy ước thay vì bằng giá trị. Kiểu đầu nét và kiểu nối nét là số nguyên, độ rộng nét bằng không có nghĩa là nét mảnh trong không gian thiết bị chứ không phải nét vô hình, và các biến thể có dấu sao của toán tử tô vẽ thay đổi quy tắc cuộn thay vì màu sắc. Mỗi điểm trong số đó cần được dịch, không phải sao chép
Một lệnh gọi cho trường hợp phổ biến
Với công việc thông thường là xuất trang cho trình xem web, công cụ so sánh khác biệt hoặc bàn giao thiết kế, bề mặt API chỉ gồm một hàm. BuildLoadedPageSVG nhận chỉ số trang đánh số từ 0 trong tài liệu hiện đang tải và trả về tài liệu SVG dưới dạng AnsiString:
var
Pdf: THotPDF;
I: Integer;
Svg: AnsiString;
Output: TFileStream;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('statements.pdf', '') <= 0 then
Exit; // LoadFromFile trả về số trang
for I := 0 to Pdf.LoadedPageCount - 1 do
begin
Svg := Pdf.BuildLoadedPageSVG(I);
if Length(Svg) = 0 then
Continue;
Output := TFileStream.Create(Format('page-%d.svg', [I + 1]), fmCreate);
try
Output.WriteBuffer(Svg[1], Length(Svg));
finally
Output.Free;
end;
end;
finally
Pdf.Free;
end;
end;
Tính năng xuất tương tự cũng được công cụ dòng lệnh của HotPDF cung cấp qua lệnh export-svg, hữu ích trong pipeline build và script hồi quy khi bạn muốn có biểu diễn trang có thể so sánh khác biệt dạng văn bản mà không cần viết bất kỳ dòng Pascal nào. Vì SVG là văn bản nên nó là bạn đồng hành tự nhiên với đường dẫn raster được mô tả trong render trang PDF thành bitmap: bitmap cho bạn biết trang trông như thế nào, còn SVG cho bạn biết trang được tạo thành từ những gì
Văn bản PDF được ánh xạ sang phần tử text SVG như thế nào?
HotPDF ghép chuỗi ma trận văn bản theo thứ tự tiền tố nhân với CTM nhân với ma trận văn bản nhân với phép lật glyph, trong đó phép lật glyph là phép nhân bên phải với matrix(1,0,0,-1,0,0). Thừa số bên phải đó tồn tại chỉ để triệt tiêu phép lật dọc ở cấp trang đối với hình dạng glyph, vì nếu không thì văn bản SVG vẽ trong khung cục bộ đã bị lật sẽ hiện ra lộn ngược. Đặt phép hiệu chỉnh vào ma trận thay vì viết mã xử lý riêng từng trường hợp có nghĩa là văn bản xoay, lật gương và nghiêng đều ra đúng mà không cần thêm nhánh rẽ
Định vị theo chiều ngang dùng cú pháp x đa giá trị của phần tử text trong SVG, một tọa độ cho mỗi ký tự, được tích lũy từ mỗi bước tiến glyph cộng thêm khoảng cách ký tự Tc và khoảng cách từ Tw đang có hiệu lực tại thời điểm đó. Tỷ lệ ngang Tz được gộp vào cột a và c của ma trận văn bản thay vì phát ra riêng, nên trình xem bỏ qua các thuộc tính văn bản đặc thù vẫn đặt đúng vị trí mỗi glyph như PDF đã đặt. Văn bản được tạo ra qua quá trình tạo hình phức tạp, được đề cập trong tạo hình văn bản kịch bản phức tạp, cũng đi theo cùng đường dẫn đó, vì bộ tạo hình đã giải quyết xong các cụm thành glyph có vị trí trước khi luồng nội dung được diễn giải
Xoay và hình ảnh: hai phép lật dễ bị làm ngược
Một trang có mục /Rotate khác 0 cần một phép biến đổi trước gồm một phép lật theo chiều cao canvas đã xoay và một phép xoay được biểu diễn trong không gian hiển thị trục y hướng lên. Ba ma trận xoay là (0,-1,1,0,0,W) cho 90 độ, (-1,0,0,-1,W,H) cho 180 độ và (0,1,-1,0,H,0) cho 270 độ, với W và H là kích thước trang trước khi xoay. Tự suy ra các ma trận này bằng tay dễ gây lỗi dấu ở đúng ba chỗ, nên bộ xuất ghép chúng thông qua cùng một hàm nhân ma trận xử lý mọi phép biến đổi khác
Hình ảnh nhúng cần phép lật riêng của chúng, vì không gian ảnh PDF đặt hàng mẫu đầu tiên ở cạnh trên của hình vuông đơn vị, trong khi phần tử image của SVG mang khung cục bộ trục y hướng xuống. Vì vậy phép biến đổi được phát ra là CTM nhân bên phải với matrix(1,0,0,-1,0,1). Làm sai điều này tạo ra ảnh chụp bị lật gương theo chiều dọc trên một trang vốn dĩ hoàn hảo, đây là loại lỗi mà người xem lại phát hiện ngay lập tức nhưng test tự động thường không phát hiện được
Thiết bị trạng thái đồ họa thực sự giữ lại những gì?
HotPDF gửi các toán tử trạng thái nét vẽ w, J, j, M và d qua một giao diện thiết bị tùy chọn riêng biệt, nên độ trung thực của nét vẽ được thêm vào mà không thay đổi vtable của thiết bị nội dung hiện có và không phá vỡ khả năng tương thích nhị phân đối với mã đã build dựa trên các phiên bản trước. Cụ thể, SVG xuất ra nhận các từ khóa đã được dịch thay vì số nguyên PDF thô:
// Các giá trị liệt kê số nguyên của PDF trở thành thuộc tính từ khóa SVG
// line cap 0, 1, 2 -> butt, round, square
// line join 0, 1, 2 -> miter, round, bevel
//
// Độ rộng nét bằng không có nghĩa là nét mảnh trong không gian thiết bị
// đối với PDF, nên bộ xuất phát ra vector-effect="non-scaling-stroke"
// để giữ nét vẽ hiển thị được và có độ rộng gần bằng một pixel thiết
// bị sau CTM
//
// f* B* b* chọn quy tắc even-odd và phát ra fill-rule="evenodd",
// còn f B b giữ nguyên mặc định nonzero winding của SVG
Khôi phục trạng thái tại một Q bao phủ đồng thời độ trong suốt, độ rộng nét, kiểu đầu nét, kiểu nối nét, giới hạn miter, mảng nét đứt và pha nét đứt. Form XObject lồng nhau chụp và khôi phục cùng bộ đầy đủ đó tại ranh giới của chúng, nên viền nét đứt được định nghĩa bên trong một con dấu không thể rò rỉ mẫu của nó sang nội dung trang phía sau. Nếu bạn đã theo dõi hành vi clipping và CTM vì lý do khác, đây chính là mô hình trạng thái tương tự xuất hiện trong nhập vector EMF và WMF, chỉ chạy theo chiều ngược lại
Giới hạn cần biết trước khi đưa vào sử dụng thực tế
Bộ xuất minh bạch về phạm vi của nó, và nắm rõ giới hạn từ đầu rẻ hơn nhiều so với phát hiện ra chúng khi đã đưa vào sản xuất. Màu sắc đến được thiết bị SVG thông qua các toán tử rg, RG, g và G. Các vùng tô được thiết lập qua không gian màu cộng với scn, đây là cách màu Separation, DeviceN và ICCBased được tô vẽ, không đến được thiết bị dưới dạng bộ ba RGB đã phân giải, nên các trang dùng màu pha theo cách này xuất ra được hình học nhưng không xuất được những màu đó. Với nguồn hướng in ấn, hãy raster hóa thay thế hoặc làm phẳng màu pha trước; bản thân mô hình tô vẽ được đề cập trong render màu pha Separation và DeviceN
Hai lưu ý nhỏ giúp tiết kiệm thời gian gỡ lỗi. Giá trị màu thập lục phân được phát ra dạng chữ hoa, nên một test khẳng định #ff0000 sẽ thất bại khi so với #FF0000 hoàn toàn đúng. Và thiết bị SVG được đếm tham chiếu thông qua giao diện của nó, nghĩa là giải phóng nó là việc để giao diện ra khỏi phạm vi thay vì gọi Free trên đối tượng, một điểm khác biệt quan trọng nếu bạn mở rộng thiết bị để phát ra mã đánh dấu riêng cùng với nội dung trang
Xuất SVG kết hợp tự nhiên với so sánh cấu trúc khi bạn cần biết liệu một tài liệu được tạo ra có thực sự thay đổi giữa hai lần build hay không. Bộ công cụ rộng hơn xoay quanh tài liệu đã tải, từ render đến chỉnh sửa đến xuất, được ghi lại trên trang thành phần HotPDF Delphi PDF