Xuất trình chiếu sang XAML trong Java
Tổng quan
Bài viết này giải thích cách xuất bản trình chiếu PowerPoint sang XAML bằng Aspose.Slides. Nó bao gồm phần giới thiệu ngắn gọn về XAML, trình bày cách lưu một bản trình chiếu dưới dạng XAML với cài đặt mặc định, và minh họa cách tùy chỉnh việc xuất qua XamlOptions, bao gồm việc xuất các slide ẩn. Bài viết cũng trả lời một số câu hỏi thường gặp liên quan đến phông chữ dự phòng, khả năng tương thích ngăn xếp XAML, và hành vi xuất slide ẩn.
Về XAML
XAML là ngôn ngữ markup dựa trên XML được dùng để mô tả giao diện người dùng trong các khung như WPF (Windows Presentation Foundation), UWP (Universal Windows Platform) và Xamarin.Forms.
Bạn có thể làm việc với các tệp XAML trong công cụ thiết kế trực quan hoặc viết và chỉnh sửa markup trực tiếp.
Xuất bản trình chiếu sang XAML với tùy chọn mặc định
Ví dụ Java sau cho thấy cách xuất một bản trình chiếu sang XAML với cài đặt mặc định:
import com.aspose.slides.*;
Presentation presentation = new Presentation("pres.pptx");
try {
XamlOptions xamlOptions = new XamlOptions();
presentation.save(xamlOptions);
} finally {
presentation.dispose();
}
Mặc định, các slide đã xuất được lưu trong thư mục con pres của thư mục làm việc hiện tại của tiến trình, được xác định từ một đường dẫn rỗng bằng Paths.get. Thư mục này được tạo tự động, và bất kỳ hình ảnh cần thiết nào cũng được lưu ở đó.
Tên thư mục đầu ra được lấy từ tên tệp nguồn mà không có phần mở rộng. Đối với pres.pptx, các tệp đầu ra sẽ có tên pres/Slide_1.xaml, pres/Slide_2.xaml và cứ như vậy. Ngay cả khi bạn truyền một đường dẫn tuyệt đối cho bản trình chiếu đầu vào, thư mục đầu ra vẫn được tạo tương đối so với thư mục làm việc hiện tại, thay vì nằm cạnh tệp đầu vào.
Xuất bản trình chiếu sang XAML với tùy chọn tùy chỉnh
Sử dụng giao diện IXamlOptions để kiểm soát cách Aspose.Slides xuất một bản trình chiếu sang XAML.
Để lưu đầu ra vào một vị trí tùy chỉnh, triển khai IXamlOutputSaver và truyền một thể hiện của triển khai của bạn vào phương thức setOutputSaver của XamlOptions.
Để bao gồm các slide ẩn trong đầu ra XAML, gọi setExportHiddenSlides với giá trị true, như trong ví dụ Java sau:
import com.aspose.slides.*;
Presentation presentation = new Presentation("pres.pptx");
try {
XamlOptions xamlOptions = new XamlOptions();
xamlOptions.setExportHiddenSlides(true);
presentation.save(xamlOptions);
} finally {
presentation.dispose();
}
Bắt giữ tất cả các tài liệu XAML được tạo
Quá trình xuất XAML có thể tạo một tài liệu XAML cho mỗi slide đã xuất cộng với các hình ảnh và tài nguyên hỗ trợ riêng biệt. Gán một IXamlOutputSaver tùy chỉnh cho XamlOptions.setOutputSaver để nhận các tài liệu này thay vì sử dụng bộ lưu mặc định trên hệ thống tệp. Bắt đầu xuất bằng phương thức overload đặc thù cho XAML của Presentation.save chấp nhận các tùy chọn XAML.
Hiểu vòng đời Callback
Bộ xuất sẽ gọi IXamlOutputSaver.save riêng biệt cho mỗi tài liệu được tạo:
pathxác định tài liệu và có thể bao gồm các thư mục tương đối. Giữ lại thông tin này vì XAML có thể tham chiếu tới tài nguyên bằng các đường dẫn tương đối.datachứa các byte của tài liệu. Hình ảnh và các tài nguyên nhị phân khác không được giải mã thành văn bản.- Bộ lưu chịu trách nhiệm giữ hoặc lưu trữ dữ liệu trước khi trả về. Các ví dụ sao chép mỗi mảng byte vào bộ nhớ thuộc về ứng dụng.
- Xem việc xuất là thành công chỉ khi thao tác lưu bản trình chiếu trả về và mọi callback đã hoàn thành thành công. Đừng bỏ qua lỗi lưu trữ hoặc bắt đầu các ghi nền không được giám sát. Nếu việc lưu diễn ra sau đó, chỉ báo cáo thành công tổng thể sau khi bước đó cũng thành công.
XamlOptions.setExportHiddenSlides cũng áp dụng cho bộ lưu tùy chỉnh. Cài đặt mặc định, false, sẽ loại bỏ các tài liệu XAML của slide ẩn. Truyền true sẽ bao gồm chúng và mọi tài nguyên cần thiết cho việc xuất. Số lượng tài nguyên phụ thuộc vào bản trình chiếu; không giả định một callback cho mỗi slide hoặc một thứ tự callback cố định.
Xuất ra bộ nhớ và kiểm tra các tài liệu
Ví dụ hoàn chỉnh này tải pres.pptx, thu thập mọi tài liệu trong một Map<String, byte[]>, và in ra tên, loại và số byte của chúng. Nó giữ nguyên các tên được cung cấp. Các tên trùng lặp sẽ làm cho bộ sưu tập không hợp lệ thay vì ghi đè âm thầm một tài liệu. Ví dụ kiểm tra điều này trước khi sử dụng kết quả.
import com.aspose.slides.*;
import java.util.LinkedHashMap;
import java.util.Map;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
class MemoryXamlSaver implements IXamlOutputSaver {
final Map<String, byte[]> artifacts = new LinkedHashMap<>();
boolean valid = true;
@Override
public void save(String path, byte[] data) {
if (artifacts.containsKey(path)) {
valid = false;
System.err.println("Export rejected: duplicate artifact name: " + path);
return;
}
byte[] retainedData = data.clone();
artifacts.put(path, retainedData);
}
}
MemoryXamlSaver saver = new MemoryXamlSaver();
Presentation presentation = new Presentation("pres.pptx");
try {
XamlOptions options = new XamlOptions();
options.setOutputSaver(saver);
options.setExportHiddenSlides(true);
presentation.save(options);
} finally {
presentation.dispose();
}
if (!saver.valid) {
System.err.println("Export rejected: the artifact collection is invalid.");
return;
}
boolean inspectXamlText = false;
for (Map.Entry<String, byte[]> artifact : saver.artifacts.entrySet()) {
String name = artifact.getKey().toLowerCase(Locale.ROOT);
boolean isXaml = name.endsWith(".xaml");
boolean isImage = name.matches(".*\\.(png|jpg|jpeg|gif|bmp|tif|tiff|svg)$");
String kind = isXaml ? "slide XAML" : isImage ? "image" : "supporting resource";
System.out.println(artifact.getKey() + ": " + artifact.getValue().length + " bytes (" + kind + ")");
// Chỉ giải mã XAML, và chỉ khi cần kiểm tra văn bản.
if (isXaml && inspectXamlText) {
String markup = new String(artifact.getValue(), StandardCharsets.UTF_8);
System.out.println(markup);
}
}
Kiểm tra phần mở rộng hữu ích cho việc kiểm tra; giữ lại tất cả các tài liệu, bao gồm cả các loại tài nguyên không quen thuộc. Để nguyên các byte khi lưu hoặc truyền chúng. Chỉ sử dụng String constructor với UTF-8 cho XAML cần xử lý văn bản.
Đóng gói các tài liệu đã thu thập vào một tệp ZIP
Ví dụ độc lập này thu thập các tài liệu xuất, xác thực tên của chúng, và ghi các byte gốc vào một tệp ZIP. Một tên tệp ZIP duy nhất tách biệt các công việc xuất đồng thời. Các mục ZIP dùng dấu gạch chéo xuôi và giữ lại các thư mục tương đối. Các tên không an toàn hoặc các tên trùng nhau sau khi chuẩn hoá sẽ bị từ chối toàn bộ gói trước khi ghi.
import com.aspose.slides.*;
import java.util.LinkedHashMap;
import java.util.Map;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
class MemoryXamlSaver implements IXamlOutputSaver {
final Map<String, byte[]> artifacts = new LinkedHashMap<>();
boolean valid = true;
@Override
public void save(String path, byte[] data) {
if (artifacts.containsKey(path)) {
valid = false;
System.err.println("Export rejected: duplicate artifact name: " + path);
return;
}
byte[] retainedData = data.clone();
artifacts.put(path, retainedData);
}
}
MemoryXamlSaver saver = new MemoryXamlSaver();
Presentation presentation = new Presentation("pres.pptx");
try {
XamlOptions options = new XamlOptions();
options.setOutputSaver(saver);
options.setExportHiddenSlides(false);
presentation.save(options);
} finally {
presentation.dispose();
}
if (!saver.valid) {
System.err.println("Export rejected: the artifact collection is invalid.");
return;
}
Map<String, byte[]> entries = new LinkedHashMap<>();
Set<String> entryNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
for (Map.Entry<String, byte[]> artifact : saver.artifacts.entrySet()) {
String entryName = artifact.getKey().replace('\\', '/');
String[] segments = entryName.split("/", -1);
boolean unsafeName = entryName.startsWith("/") || entryName.contains(":");
for (String segment : segments) {
unsafeName |= segment.trim().isEmpty() || segment.equals(".") || segment.equals("..");
}
if (unsafeName || !entryNames.add(entryName)) {
System.err.println("Export rejected: unsafe or duplicate artifact name: " + artifact.getKey());
return;
}
entries.put(entryName, artifact.getValue());
}
Path archivePath = Paths.get("xaml-" + UUID.randomUUID() + ".zip");
try {
OutputStream output = Files.newOutputStream(archivePath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE);
try (OutputStream archiveOutput = output; ZipOutputStream archive = new ZipOutputStream(archiveOutput)) {
for (Map.Entry<String, byte[]> artifact : entries.entrySet()) {
ZipEntry entry = new ZipEntry(artifact.getKey());
archive.putNextEntry(entry);
archive.write(artifact.getValue());
archive.closeEntry();
}
}
// Thư mục ZIP đã được hoàn tất bằng cách đóng trước khi báo cáo thành công.
System.out.println("Saved " + entries.size() + " artifacts to " + archivePath);
} catch (IOException exception) {
System.err.println("Archive persistence failed: " + exception.getMessage());
}
Ví dụ sử dụng ZipOutputStream để ghi một kho lưu trữ cục bộ; bộ xuất không ghi các tệp XAML hoặc hình ảnh rải rác. Đối với lưu trữ từ xa, thay thế giai đoạn ghi kho lưu trữ bằng việc tải lên các mảng byte đã thu thập. Sử dụng một định danh công việc xuất cộng với tên tài liệu tương đối đầy đủ làm khóa blob, hoặc lưu định danh công việc, tên tương đối và dữ liệu nhị phân trong một bản ghi cơ sở dữ liệu. Phát hành công việc chỉ sau khi tất cả các tải lên hoàn tất hoặc giao dịch cơ sở dữ liệu được cam kết. Dọn dẹp đầu ra một phần nếu việc lưu trữ thất bại.
Đối với các bản trình chiếu lớn, một bộ lưu tùy chỉnh có thể lưu trữ mỗi tài liệu trực tiếp vào bộ nhớ lưu trữ của ứng dụng để tránh giữ một bản sao bổ sung của toàn bộ xuất trong bộ nhớ ứng dụng. Giữ mỗi callback đồng bộ từ quan điểm của bộ xuất: trả về chỉ sau khi đích nhận được các byte, và cho phép lỗi truyền tới người gọi.
Bảo tồn tên tài nguyên và xác minh các tham chiếu
- Chuẩn hoá dấu tách đường dẫn khi đích yêu cầu, nhưng vẫn giữ các thư mục tương đối. Đừng chỉ dùng Path.getFileName trừ khi mọi tên tạo ra đều được biết là duy nhất và các tham chiếu tài nguyên vẫn hợp lệ.
- Áp dụng kiểm tra tên đặc thù cho đích. Khi ghi các tệp rải rác, từ chối các đường dẫn gốc và các đoạn di chuyển, giải quyết đích bằng Path.toAbsolutePath, và xác nhận nó vẫn ở dưới thư mục xuất dự định, bao gồm dấu tách thư mục trong kiểm tra bao hàm. Sử dụng thư mục do ứng dụng kiểm soát mà không có liên kết ký hiệu có thể chuyển hướng ghi.
- Sử dụng một bộ lưu và không gian tên lưu trữ riêng cho mỗi công việc xuất. Phát hiện va chạm sau khi chuẩn hoá dấu tách và theo quy tắc phân biệt ký tự hoa/thường của đích.
- Trước khi phát hành, phân tích mỗi tài liệu XAML như XML và kiểm tra các tham chiếu tài nguyên dựa trên tệp, chẳng hạn thuộc tính
SourcehoặcImageSourcecủa hình ảnh. Giải quyết mỗi URI tương đối dựa trên thư mục của tài liệu XAML chứa, chuẩn hoá tên lưu trữ kết quả, và xác nhận khóa map, mục ZIP hoặc đối tượng lưu trữ tương ứng tồn tại. Xử lý các URI bên ngoài và các biểu thức markup XAML riêng biệt so với các tên tệp tương đối.
Ví dụ, nếu pres/Slide_1.xaml tham chiếu tới images/image1.png, tài nguyên đã lưu phải có sẵn dưới dạng pres/images/image1.png. Giữ chỉ image1.png sẽ phá vỡ mối quan hệ đó. Đối với lưu trữ đối tượng, bảo tồn cùng cấu trúc dưới tiền tố công việc và làm cho các URL tài nguyên đó có thể truy cập được cho người tiêu thụ XAML. Mở lại ZIP đã hoàn thành để xác minh tên mục và byte tài nguyên, và tải các slide đại diện trong môi trường XAML mục tiêu để xác nhận hình ảnh được giải quyết đúng.
Câu hỏi thường gặp
Làm thế nào để đảm bảo phông chữ dự đoán được nếu phông chữ gốc không có trên máy?
Gọi setDefaultRegularFont trong XamlOptions — nó được dùng làm phông chữ dự phòng trong quá trình xuất khi phông chữ gốc thiếu. Điều này không đảm bảo rằng XAML được tạo sẽ tham chiếu tới phông chữ dự phòng hoặc rằng phông chữ đó có sẵn trên máy đích. Đảm bảo các phông chữ mà XAML tham chiếu đã được cài đặt trong môi trường nơi nó được hiển thị.
XAML xuất ra chỉ dành cho WPF hay có thể dùng được trong các ngăn xếp XAML khác không?
Aspose.Slides xuất XAML WPF thông qua API công khai của nó. Khả năng tương thích với các ngăn xếp XAML khác, chẳng hạn UWP và Xamarin.Forms, không được đảm bảo. Hãy kiểm tra markup được tạo trong môi trường đích của bạn.
Slide ẩn có được hỗ trợ không, và làm sao để ngăn chúng bị xuất mặc định?
Mặc định, các slide ẩn không được bao gồm. Bạn có thể kiểm soát hành vi này qua setExportHiddenSlides trong XamlOptions — giữ nó ở trạng thái tắt nếu bạn không cần xuất các slide này.