feat: 实现 DOCX 媒体预处理与跨端 PNG 捕获

This commit is contained in:
SkyJourney
2026-07-30 13:21:18 +08:00
parent 7831bc23f0
commit fe5d67fb6d
20 changed files with 1294 additions and 16 deletions
@@ -0,0 +1,75 @@
import type { Page } from "playwright";
import type {
DocxMediaCaptureAdapter,
DocxMediaCaptureRequest
} from "@md-to-pdf/application";
import type { DocxMediaCapturePlan } from "@md-to-pdf/core";
interface CaptureScreenshotResult {
data: string;
}
async function renderCapturePlan(
page: Page,
request: DocxMediaCaptureRequest
) {
return page.evaluate(
async ({ payload, dimensions }): Promise<DocxMediaCapturePlan> => {
const renderer = (
window as typeof window & {
__mdToPdfRenderDocxMedia?: (
renderPayload: typeof payload,
renderDimensions: typeof dimensions
) => Promise<DocxMediaCapturePlan>;
}
).__mdToPdfRenderDocxMedia;
if (!renderer) {
throw new Error("DOCX 媒体渲染运行时不可用");
}
return renderer(payload, dimensions);
},
request
);
}
export async function captureDocxMediaWithPlaywrightPage(
page: Page,
request: DocxMediaCaptureRequest
) {
const plan = await renderCapturePlan(page, request);
const session = await page.context().newCDPSession(page);
try {
const captures = [];
for (const target of plan.targets) {
const result = (await session.send("Page.captureScreenshot", {
format: "png",
fromSurface: true,
captureBeyondViewport: true,
clip: {
x: target.captureX,
y: target.captureY,
width: target.captureWidthPx,
height: target.captureHeightPx,
scale: target.rasterScale
}
})) as CaptureScreenshotResult;
captures.push({
id: target.id,
png: Buffer.from(result.data, "base64")
});
}
return { plan, captures };
} finally {
await session.detach();
}
}
export class PlaywrightDocxMediaCaptureAdapter
implements DocxMediaCaptureAdapter
{
constructor(private readonly page: Page) {}
capture(request: DocxMediaCaptureRequest) {
return captureDocxMediaWithPlaywrightPage(this.page, request);
}
}