fix: 修复预览与 PDF 渲染边界问题
This commit is contained in:
@@ -608,6 +608,9 @@ export function App() {
|
||||
}
|
||||
|
||||
function handlePreviewModeChange(mode: PreviewMode) {
|
||||
if (mode === previewMode) {
|
||||
return;
|
||||
}
|
||||
setPreviewMode(mode);
|
||||
setPreviewFrameReady(false);
|
||||
setPreviewPagination({ current: 0, total: 0 });
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
getPaperDimensionsMm,
|
||||
lengthToMillimeters,
|
||||
paperFormatLabels,
|
||||
supportedMermaidLayouts,
|
||||
supportedMermaidLooks,
|
||||
@@ -76,10 +77,6 @@ const mermaidFontFamilies = [
|
||||
"Consolas, Microsoft YaHei, monospace"
|
||||
] as const;
|
||||
|
||||
function millimeters(value: string) {
|
||||
return Number.parseFloat(value.replace(/mm$/i, ""));
|
||||
}
|
||||
|
||||
function fitMarginPair(
|
||||
first: number,
|
||||
second: number,
|
||||
@@ -103,16 +100,18 @@ function LengthInput({
|
||||
maximum,
|
||||
onChange
|
||||
}: LengthInputProps) {
|
||||
const [draft, setDraft] = useState(String(millimeters(value)));
|
||||
const [draft, setDraft] = useState(
|
||||
String(lengthToMillimeters(value))
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setDraft(String(millimeters(value)));
|
||||
setDraft(String(lengthToMillimeters(value)));
|
||||
}, [value]);
|
||||
|
||||
function commit() {
|
||||
const number = Number.parseFloat(draft);
|
||||
if (!Number.isFinite(number)) {
|
||||
setDraft(String(millimeters(value)));
|
||||
setDraft(String(lengthToMillimeters(value)));
|
||||
return;
|
||||
}
|
||||
const normalized = Math.min(Math.max(number, 0), maximum);
|
||||
@@ -231,13 +230,13 @@ export function ExportSettingsDrawer({
|
||||
nextPaper.orientation
|
||||
);
|
||||
const [left, right] = fitMarginPair(
|
||||
millimeters(nextPaper.margins.left),
|
||||
millimeters(nextPaper.margins.right),
|
||||
lengthToMillimeters(nextPaper.margins.left),
|
||||
lengthToMillimeters(nextPaper.margins.right),
|
||||
nextDimensions.width - 1
|
||||
);
|
||||
const [top, bottom] = fitMarginPair(
|
||||
millimeters(nextPaper.margins.top),
|
||||
millimeters(nextPaper.margins.bottom),
|
||||
lengthToMillimeters(nextPaper.margins.top),
|
||||
lengthToMillimeters(nextPaper.margins.bottom),
|
||||
nextDimensions.height - 1
|
||||
);
|
||||
|
||||
@@ -380,7 +379,9 @@ export function ExportSettingsDrawer({
|
||||
label="上"
|
||||
value={margins.top}
|
||||
maximum={
|
||||
dimensions.height - millimeters(margins.bottom) - verticalGap
|
||||
dimensions.height -
|
||||
lengthToMillimeters(margins.bottom) -
|
||||
verticalGap
|
||||
}
|
||||
onChange={(value) => updateMargin("top", value)}
|
||||
/>
|
||||
@@ -388,7 +389,9 @@ export function ExportSettingsDrawer({
|
||||
label="右"
|
||||
value={margins.right}
|
||||
maximum={
|
||||
dimensions.width - millimeters(margins.left) - horizontalGap
|
||||
dimensions.width -
|
||||
lengthToMillimeters(margins.left) -
|
||||
horizontalGap
|
||||
}
|
||||
onChange={(value) => updateMargin("right", value)}
|
||||
/>
|
||||
@@ -396,7 +399,9 @@ export function ExportSettingsDrawer({
|
||||
label="下"
|
||||
value={margins.bottom}
|
||||
maximum={
|
||||
dimensions.height - millimeters(margins.top) - verticalGap
|
||||
dimensions.height -
|
||||
lengthToMillimeters(margins.top) -
|
||||
verticalGap
|
||||
}
|
||||
onChange={(value) => updateMargin("bottom", value)}
|
||||
/>
|
||||
@@ -404,7 +409,9 @@ export function ExportSettingsDrawer({
|
||||
label="左"
|
||||
value={margins.left}
|
||||
maximum={
|
||||
dimensions.width - millimeters(margins.right) - horizontalGap
|
||||
dimensions.width -
|
||||
lengthToMillimeters(margins.right) -
|
||||
horizontalGap
|
||||
}
|
||||
onChange={(value) => updateMargin("left", value)}
|
||||
/>
|
||||
|
||||
@@ -2,10 +2,14 @@ import highlightCss from "highlight.js/styles/github.css?inline";
|
||||
import katexCss from "katex/dist/katex.min.css?inline";
|
||||
import { Previewer } from "pagedjs";
|
||||
import { createMermaidSiteConfig } from "./mermaid-config";
|
||||
import { fitOversizedMermaidToPage } from "./mermaid-page-fit";
|
||||
import {
|
||||
fitOversizedMermaidToPage,
|
||||
getPageContentDimensions
|
||||
} from "./mermaid-page-fit";
|
||||
import { renderMermaidDefinitions } from "./mermaid-renderer";
|
||||
import { replaceMermaidSvgWithImages } from "./mermaid-static-image";
|
||||
import type { MermaidOutputMode } from "./mermaid-static-image";
|
||||
import { enablePrintMediaForPreview } from "./preview-styles";
|
||||
import "./paged-table-handler";
|
||||
import {
|
||||
buildPagedMediaCss,
|
||||
@@ -70,6 +74,54 @@ function stylesheet(
|
||||
};
|
||||
}
|
||||
|
||||
function mountMeasurementContainer(
|
||||
documentRef: Document,
|
||||
content: DocumentFragment,
|
||||
payload: PagedPreviewPayload,
|
||||
target: PagedRenderTarget
|
||||
) {
|
||||
const pageContent = getPageContentDimensions(payload.exportConfig);
|
||||
const style = documentRef.createElement("style");
|
||||
const themeCss =
|
||||
target === "preview"
|
||||
? enablePrintMediaForPreview(payload.themeCss)
|
||||
: payload.themeCss;
|
||||
style.dataset.pagedMeasurementStyle = "true";
|
||||
style.textContent = [
|
||||
documentBaseCss,
|
||||
highlightCss,
|
||||
katexCss,
|
||||
themeCss,
|
||||
documentGeometryCss
|
||||
].join("\n");
|
||||
|
||||
const host = documentRef.createElement("div");
|
||||
host.dataset.pagedMeasurementHost = "true";
|
||||
host.style.cssText = [
|
||||
"position: fixed",
|
||||
"left: -100000px",
|
||||
"top: 0",
|
||||
`width: ${pageContent.width}px`,
|
||||
"height: auto",
|
||||
"opacity: 0",
|
||||
"pointer-events: none",
|
||||
"contain: layout style paint"
|
||||
].join(";");
|
||||
|
||||
documentRef.head.append(style);
|
||||
host.append(content);
|
||||
documentRef.body.append(host);
|
||||
|
||||
return {
|
||||
host,
|
||||
restore() {
|
||||
content.append(...Array.from(host.childNodes));
|
||||
host.remove();
|
||||
style.remove();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function applyPageNumbers(
|
||||
container: ParentNode,
|
||||
payload: PagedPreviewPayload,
|
||||
@@ -233,16 +285,37 @@ export class PagedDocumentRuntime {
|
||||
const mermaidErrors = await this.renderMermaid(content, payload);
|
||||
const mermaidMs = performance.now() - mermaidStartedAt;
|
||||
|
||||
const mermaidFitStartedAt = performance.now();
|
||||
fitOversizedMermaidToPage(content, payload.exportConfig);
|
||||
const mermaidFitMs = performance.now() - mermaidFitStartedAt;
|
||||
let mermaidFitMs = 0;
|
||||
let mermaidConversionMs = 0;
|
||||
if (content.querySelector(".mermaid svg")) {
|
||||
const measurement = mountMeasurementContainer(
|
||||
documentRef,
|
||||
content,
|
||||
payload,
|
||||
options.target
|
||||
);
|
||||
try {
|
||||
await documentRef.fonts.ready;
|
||||
await waitForImages(measurement.host);
|
||||
|
||||
const mermaidConversionStartedAt = performance.now();
|
||||
if (options.mermaidOutput === "svg-image") {
|
||||
replaceMermaidSvgWithImages(content);
|
||||
const mermaidFitStartedAt = performance.now();
|
||||
fitOversizedMermaidToPage(
|
||||
measurement.host,
|
||||
payload.exportConfig
|
||||
);
|
||||
mermaidFitMs = performance.now() - mermaidFitStartedAt;
|
||||
|
||||
const mermaidConversionStartedAt = performance.now();
|
||||
if (options.mermaidOutput === "svg-image") {
|
||||
replaceMermaidSvgWithImages(measurement.host);
|
||||
await waitForImages(measurement.host);
|
||||
}
|
||||
mermaidConversionMs =
|
||||
performance.now() - mermaidConversionStartedAt;
|
||||
} finally {
|
||||
measurement.restore();
|
||||
}
|
||||
}
|
||||
const mermaidConversionMs =
|
||||
performance.now() - mermaidConversionStartedAt;
|
||||
|
||||
if (!shouldContinue()) {
|
||||
return undefined;
|
||||
|
||||
Reference in New Issue
Block a user