zt-tiptap-export-pdf 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -142,6 +142,47 @@ async function createCorsImageElement(src) {
142
142
  imageElement.src = src;
143
143
  });
144
144
  }
145
+ /** 将图片 Blob 读取为 data URL。 */
146
+ function readBlobAsDataUrl(blob) {
147
+ return new Promise((resolve) => {
148
+ // Blob 读取器。
149
+ const reader = new FileReader();
150
+ /** 处理 Blob 读取完成。 */
151
+ function handleLoad() {
152
+ // 读取结果。
153
+ const result = reader.result;
154
+ resolve(typeof result === "string" ? result : null);
155
+ }
156
+ /** 处理 Blob 读取失败。 */
157
+ function handleError() {
158
+ resolve(null);
159
+ }
160
+ reader.addEventListener("load", handleLoad);
161
+ reader.addEventListener("error", handleError);
162
+ reader.readAsDataURL(blob);
163
+ });
164
+ }
165
+ /** 通过 fetch 下载可跨域读取的图片 data URL。 */
166
+ async function fetchImageDataUrl(src) {
167
+ try {
168
+ // 图片响应。
169
+ const response = await fetch(src, { mode: "cors" });
170
+ if (!response.ok) {
171
+ return null;
172
+ }
173
+ // 响应内容类型。
174
+ const contentType = response.headers.get("content-type") || "";
175
+ if (!contentType.toLowerCase().startsWith("image/")) {
176
+ return null;
177
+ }
178
+ // 图片二进制内容。
179
+ const imageBlob = await response.blob();
180
+ return readBlobAsDataUrl(imageBlob);
181
+ }
182
+ catch {
183
+ return null;
184
+ }
185
+ }
145
186
  /** 将图片节点转为 PNG data URL。 */
146
187
  async function getImageDataUrl(imageElement, widthPx, heightPx) {
147
188
  // 图片地址。
@@ -156,7 +197,12 @@ async function getImageDataUrl(imageElement, widthPx, heightPx) {
156
197
  }
157
198
  // 跨域图片副本。
158
199
  const corsImageElement = await createCorsImageElement(imageSrc);
159
- return corsImageElement ? drawImageToDataUrl(corsImageElement, widthPx, heightPx) : null;
200
+ // 跨域副本 data URL。
201
+ const corsImageDataUrl = corsImageElement ? drawImageToDataUrl(corsImageElement, widthPx, heightPx) : null;
202
+ if (corsImageDataUrl) {
203
+ return corsImageDataUrl;
204
+ }
205
+ return fetchImageDataUrl(imageSrc);
160
206
  }
161
207
  /** 读取图片说明文本。 */
162
208
  function getImageCaptionText(element) {
@@ -10,7 +10,6 @@ export declare const DEFAULT_EXPORT_FONT_FAMILY = "NotoSansSC";
10
10
  export declare const DEFAULT_LINE_HEIGHT_FACTOR = 1.5;
11
11
  export declare const DEFAULT_BLOCK_MARGIN_BOTTOM_PT = 8;
12
12
  export declare const DEFAULT_BLOCKQUOTE_INDENT_PT = 14;
13
- export declare const BLOCKQUOTE_LINE_GAP_PT = 8;
14
13
  export declare const BLOCKQUOTE_LINE_WIDTH_PT = 1;
15
14
  export declare const CODE_BLOCK_FILL_GRAY = 240;
16
15
  export declare const CODE_BLOCK_PADDING_X_PT = 8;
@@ -23,8 +23,6 @@ export const DEFAULT_LINE_HEIGHT_FACTOR = 1.5;
23
23
  export const DEFAULT_BLOCK_MARGIN_BOTTOM_PT = 8;
24
24
  // 引用块默认左侧缩进(pt)。
25
25
  export const DEFAULT_BLOCKQUOTE_INDENT_PT = 14;
26
- // 引用块左侧竖线与文本的间隔(pt)。
27
- export const BLOCKQUOTE_LINE_GAP_PT = 8;
28
26
  // 引用块左侧竖线宽度(pt)。
29
27
  export const BLOCKQUOTE_LINE_WIDTH_PT = 1;
30
28
  // 代码块背景灰度值。
@@ -1,4 +1,4 @@
1
- import { BLOCKQUOTE_LINE_GAP_PT, BLOCKQUOTE_LINE_WIDTH_PT } from "../exportConstants";
1
+ import { BLOCKQUOTE_LINE_WIDTH_PT } from "../exportConstants";
2
2
  import { splitTextToLines } from "../exportText";
3
3
  import { ensureLineSpace } from "./shared";
4
4
  /** 写入引用块。 */
@@ -7,12 +7,10 @@ export function writeBlockquoteTextBlock(pdf, cursor, { text, style, fontFamily
7
7
  pdf.setFontSize(style.fontSizePt);
8
8
  // 引用块文本左侧缩进(pt)。
9
9
  const quoteIndentPt = style.indentLeftPt || 0;
10
- // 引用块竖线与文本总缩进(pt)。
11
- const quoteTextIndentPt = quoteIndentPt + BLOCKQUOTE_LINE_GAP_PT;
12
10
  // 引用块文本写入 x 坐标。
13
- const quoteTextLeftPt = cursor.leftPt + quoteTextIndentPt;
11
+ const quoteTextLeftPt = cursor.leftPt + quoteIndentPt;
14
12
  // 引用块文本可用宽度。
15
- const quoteTextWidthPt = cursor.contentWidthPt - quoteTextIndentPt;
13
+ const quoteTextWidthPt = cursor.contentWidthPt - quoteIndentPt;
16
14
  // 引用块文本行列表。
17
15
  const quoteLines = text.split("\n").flatMap((lineText) => splitTextToLines(pdf, lineText, quoteTextWidthPt));
18
16
  quoteLines.forEach((lineText) => {
@@ -35,16 +35,17 @@ export function writeCodeTextBlock(pdf, cursor, { text, style, fontFamily }) {
35
35
  const codeLines = getCodeBlockLines(pdf, text, codeTextWidthPt);
36
36
  // 代码块总高度(pt)。
37
37
  const codeBlockHeightPt = codeLines.length * style.lineHeightPt + codePaddingYPt * 2;
38
- if (cursor.yPt + codeBlockHeightPt > cursor.bottomPt) {
38
+ // 文本基线偏移。
39
+ const textBaselineOffsetPt = style.lineHeightPt * 0.8;
40
+ // 代码块背景顶部 y 坐标(将“当前文本基线”换算为“块顶”坐标)。
41
+ let codeBlockTopYPt = Math.max(cursor.yPt - textBaselineOffsetPt, PDF_TOP_MARGIN_PT);
42
+ if (codeBlockTopYPt + codeBlockHeightPt > cursor.bottomPt) {
39
43
  pdf.addPage();
40
44
  cursor.yPt = PDF_TOP_MARGIN_PT;
45
+ codeBlockTopYPt = cursor.yPt;
41
46
  }
42
- // 代码块背景顶部 y 坐标。
43
- const codeBlockTopYPt = cursor.yPt;
44
47
  pdf.setFillColor(CODE_BLOCK_FILL_GRAY, CODE_BLOCK_FILL_GRAY, CODE_BLOCK_FILL_GRAY);
45
48
  pdf.rect(cursor.leftPt, codeBlockTopYPt, cursor.contentWidthPt, codeBlockHeightPt, "F");
46
- // 文本基线偏移。
47
- const textBaselineOffsetPt = style.lineHeightPt * 0.8;
48
49
  // 代码块文本当前基线 y 坐标。
49
50
  let lineBaselineYPt = codeBlockTopYPt + codePaddingYPt + textBaselineOffsetPt;
50
51
  codeLines.forEach((lineText) => {
@@ -50,14 +50,19 @@ export function writeImageTextBlock(pdf, cursor, { imageContent, imageCaptionTex
50
50
  const captionBottomGapPt = captionLines.length > 0 ? IMAGE_CAPTION_BOTTOM_GAP_PT : 0;
51
51
  // 图片块整体高度(pt)。
52
52
  const blockHeightPt = imageHeightPt + captionBaselineGapPt + captionHeightPt + captionBottomGapPt;
53
+ // 文本基线偏移(用于将文本流基线换算为块顶坐标)。
54
+ const textBaselineOffsetPt = style.lineHeightPt * 0.8;
55
+ // 图片块顶部 y 坐标。
56
+ let imageBlockTopYPt = Math.max(cursor.yPt - textBaselineOffsetPt, PDF_TOP_MARGIN_PT);
53
57
  // 图片写入 x 坐标。
54
58
  const imageLeftPt = getImageLeftPt(imageContent, cursor, imageWidthPt);
55
- if (cursor.yPt + blockHeightPt > cursor.bottomPt) {
59
+ if (imageBlockTopYPt + blockHeightPt > cursor.bottomPt) {
56
60
  pdf.addPage();
57
61
  cursor.yPt = PDF_TOP_MARGIN_PT;
62
+ imageBlockTopYPt = cursor.yPt;
58
63
  }
59
- pdf.addImage(imageContent.dataUrl, "PNG", imageLeftPt, cursor.yPt, imageWidthPt, imageHeightPt);
60
- cursor.yPt += imageHeightPt + captionBaselineGapPt;
64
+ pdf.addImage(imageContent.dataUrl, "PNG", imageLeftPt, imageBlockTopYPt, imageWidthPt, imageHeightPt);
65
+ cursor.yPt = imageBlockTopYPt + imageHeightPt + captionBaselineGapPt;
61
66
  if (captionLines.length > 0) {
62
67
  pdf.setFont(fontFamily, style.fontStyle);
63
68
  pdf.setFontSize(style.fontSizePt);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zt-tiptap-export-pdf",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Export editor DOM content to PDF in browser using jsPDF",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",