feat: 实现 Chromium PDF 导出与精确预览

This commit is contained in:
SkyJourney
2026-07-26 13:21:17 +08:00
parent d62552384f
commit d1b5880a5b
26 changed files with 3328 additions and 316 deletions
+168
View File
@@ -184,6 +184,31 @@ h1 {
white-space: nowrap;
}
.preview-mode-control {
display: inline-flex;
flex: none;
padding: 2px;
border: 1px solid #c9d4ce;
border-radius: 8px;
background: #eef2ef;
}
.preview-mode-control button {
min-height: 28px;
padding: 0 9px;
border: 0;
border-radius: 6px;
background: transparent;
color: #68766f;
font-size: 0.69rem;
}
.preview-mode-control button.is-active {
background: #fff;
color: #356c55;
box-shadow: 0 1px 4px rgb(37 49 43 / 12%);
}
.theme-control {
display: flex;
min-width: 0;
@@ -290,6 +315,145 @@ textarea:focus {
font-size: 0.9rem;
}
.precise-pdf-document {
display: flex;
min-width: 100%;
flex-direction: column;
align-items: center;
gap: 24px;
padding: 34px 30px;
}
.precise-pdf-page {
position: relative;
flex: none;
max-width: 100%;
overflow: hidden;
background: #fff;
box-shadow: 0 18px 48px rgb(37 49 43 / 16%);
}
.precise-pdf-page canvas,
.precise-pdf-page .textLayer {
position: absolute;
inset: 0;
}
.precise-pdf-page canvas {
display: block;
max-width: 100%;
opacity: 0;
transition: opacity 80ms ease-out;
}
.precise-pdf-page canvas.is-rendered {
opacity: 1;
}
.precise-pdf-page .textLayer {
z-index: 1;
overflow: hidden;
color-scheme: only light;
opacity: 1;
line-height: 1;
letter-spacing: normal;
word-spacing: normal;
text-size-adjust: none;
forced-color-adjust: none;
transform-origin: 0 0;
caret-color: CanvasText;
--min-font-size: 1;
--text-scale-factor:
calc(var(--total-scale-factor) * var(--min-font-size));
--min-font-size-inv: calc(1 / var(--min-font-size));
}
.precise-pdf-page .textLayer :is(span, br) {
position: absolute;
color: transparent;
white-space: pre;
cursor: text;
user-select: text;
transform-origin: 0 0;
}
.precise-pdf-page .textLayer > :not(.markedContent),
.precise-pdf-page
.textLayer
.markedContent
span:not(.markedContent) {
z-index: 1;
font-size:
calc(var(--text-scale-factor) * var(--font-height, 0));
transform:
rotate(var(--rotate, 0deg))
scaleX(var(--scale-x, 1))
scale(var(--min-font-size-inv));
}
.precise-pdf-page .textLayer .markedContent {
display: contents;
}
.precise-pdf-page .textLayer span[role="img"] {
cursor: default;
user-select: none;
}
.precise-pdf-page .textLayer ::selection {
color: transparent;
background: rgb(0 100 255 / 25%);
}
.precise-pdf-page .textLayer br::selection {
background: transparent;
}
.precise-pdf-page .textLayer .endOfContent {
position: absolute;
display: block;
z-index: 0;
inset: 100% 0 0;
cursor: default;
user-select: none;
}
.precise-pdf-page .textLayer.selecting .endOfContent {
top: 0;
}
.precise-pdf-page-placeholder,
.precise-pdf-page-error {
position: absolute;
display: grid;
padding: 20px;
inset: 0;
place-items: center;
color: #7a8580;
background: #fff;
font-size: 0.82rem;
}
.precise-pdf-page-error {
z-index: 2;
color: #a23d3d;
background: rgb(255 255 255 / 92%);
}
.precise-pdf-status {
display: grid;
align-content: center;
gap: 14px;
min-height: 500px;
place-items: center;
color: #6f7d76;
font-size: 0.86rem;
}
.precise-pdf-status.is-error {
color: #a23d3d;
}
.settings-layer {
position: fixed;
z-index: 20;
@@ -607,6 +771,10 @@ textarea:focus {
max-width: 62%;
}
.preview-heading-actions {
flex-wrap: wrap;
}
.theme-control select {
max-width: 150px;
}