Skip to content

Commit

Permalink
feat(doc): support paste img by right context
Browse files Browse the repository at this point in the history
  • Loading branch information
Gggpound committed Nov 21, 2024
1 parent 3e828d1 commit eb47fc5
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -255,25 +255,7 @@ export class DocDrawingUpdateRenderController extends Disposable implements IRen
const res = this._getCurrentSceneAndTransformer();
if (res && res.transformer) {
this.disposeWithMe(res.transformer.changeEnd$.pipe(debounceTime(30)).subscribe((params) => {
const activeTextRange = this._docSelectionManagerService.getActiveTextRange();
if (activeTextRange) {
const { startOffset, endOffset } = activeTextRange;
if (startOffset + 1 !== endOffset) {
return;
}
const customBlocks = this._context.unit.getBody()?.customBlocks ?? [];
const block = customBlocks.find((b) => b.startIndex === startOffset);
if (block) {
this._setDrawingSelections([{
drawingId: block.blockId,
drawingType: 5,
unitId: this._context.unit.getUnitId(),
subUnitId: this._context.unit.getUnitId(),
}]);
}
}

// this._setDrawingSelections(params);
this._docSelectionManagerService.refreshSelection();
}));
} else {
throw new Error('transformer is not init');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@ import {
Inject,
RxDisposable,
} from '@univerjs/core';
import { FILES_CLIPBOARD_MIME_TYPE, HTML_CLIPBOARD_MIME_TYPE, PLAIN_TEXT_CLIPBOARD_MIME_TYPE } from '@univerjs/ui';
import {
FILE__BMP_CLIPBOARD_MIME_TYPE,
FILE__JPEG_CLIPBOARD_MIME_TYPE,
FILE__WEBP_CLIPBOARD_MIME_TYPE,
FILE_PNG_CLIPBOARD_MIME_TYPE,
HTML_CLIPBOARD_MIME_TYPE,
PLAIN_TEXT_CLIPBOARD_MIME_TYPE,
} from '@univerjs/ui';
import { takeUntil } from 'rxjs';
import { whenDocOrEditor } from '../../commands/commands/clipboard.command';
import { IDocClipboardService } from '../../services/clipboard/clipboard.service';
Expand Down Expand Up @@ -59,8 +66,16 @@ export class DocClipboardController extends RxDisposable implements IRenderModul
const clipboardEvent = config!.event as ClipboardEvent;
let htmlContent = clipboardEvent.clipboardData?.getData(HTML_CLIPBOARD_MIME_TYPE);
const textContent = clipboardEvent.clipboardData?.getData(PLAIN_TEXT_CLIPBOARD_MIME_TYPE);

const imageTypes = [
FILE__BMP_CLIPBOARD_MIME_TYPE,
FILE__JPEG_CLIPBOARD_MIME_TYPE,
FILE__WEBP_CLIPBOARD_MIME_TYPE,
FILE_PNG_CLIPBOARD_MIME_TYPE,
];

const files = [...(clipboardEvent.clipboardData?.items || [])]
.filter((item) => item.kind === FILES_CLIPBOARD_MIME_TYPE && item.type === 'image/png')
.filter((item) => imageTypes.includes(item.type))
.map((item) => item.getAsFile()!)
.filter((e) => !!e);

Expand Down
54 changes: 39 additions & 15 deletions packages/docs-ui/src/services/clipboard/clipboard.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ import type { IRectRangeWithStyle, ITextRangeWithStyle } from '@univerjs/engine-
import { BuildTextUtils, createIdentifier, DataStreamTreeTokenType, Disposable, DOC_RANGE_TYPE, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, getBodySlice, ICommandService, ILogService, Inject, IUniverInstanceService, normalizeBody, ObjectRelativeFromH, ObjectRelativeFromV, PositionedObjectLayoutType, SliceBodyType, toDisposable, Tools, UniverInstanceType } from '@univerjs/core';
import { DocSelectionManagerService } from '@univerjs/docs';
import { DrawingTypeEnum, ImageSourceType } from '@univerjs/drawing';
import { HTML_CLIPBOARD_MIME_TYPE, IClipboardInterfaceService, PLAIN_TEXT_CLIPBOARD_MIME_TYPE } from '@univerjs/ui';
import {
FILE__BMP_CLIPBOARD_MIME_TYPE,
FILE__JPEG_CLIPBOARD_MIME_TYPE,
FILE__WEBP_CLIPBOARD_MIME_TYPE,
FILE_PNG_CLIPBOARD_MIME_TYPE,
HTML_CLIPBOARD_MIME_TYPE,
IClipboardInterfaceService,
PLAIN_TEXT_CLIPBOARD_MIME_TYPE,
} from '@univerjs/ui';
import { CutContentCommand, InnerPasteCommand } from '../../commands/commands/clipboard.inner.command';
import { getCursorWhenDelete } from '../../commands/commands/doc-delete.command';
import { copyContentCache, extractId, genId } from './copy-content-cache';
Expand All @@ -41,6 +49,7 @@ export interface IDocClipboardHook {
onCopyProperty?(start: number, end: number): IClipboardPropertyItem;
onCopyContent?(start: number, end: number): string;
onBeforePaste?: (body: IDocumentBody) => IDocumentBody;
onBeforePasteImage?: (file: File) => Promise<{ source: string; imageSourceType: ImageSourceType }>;
}

export interface IDocClipboardService {
Expand Down Expand Up @@ -138,9 +147,7 @@ export class DocClipboardService extends Disposable implements IDocClipboardServ
const currentDocInstance = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_DOC);
const docUnitId = currentDocInstance?.getUnitId() || '';
if (!html && !text && files.length) {
// 粘贴图片
const imageHtml = await this._createImagePasteHtml(files);
html = imageHtml;
html = await this._createImagePasteHtml(files);
}
const partDocData = this._genDocDataFromHtmlAndText(html, text, docUnitId);
// Paste in sheet editing mode without paste style, so we give textRuns empty array;
Expand Down Expand Up @@ -411,10 +418,9 @@ export class DocClipboardService extends Disposable implements IDocClipboardServ

private async _genDocDataFromClipboardItems(items: ClipboardItem[]): Promise<Partial<IDocumentData>> {
try {
// TODO: support paste image.

let html = '';
let text = '';
const files: File[] = [];
for (const clipboardItem of items) {
for (const type of clipboardItem.types) {
switch (type) {
Expand All @@ -426,9 +432,21 @@ export class DocClipboardService extends Disposable implements IDocClipboardServ
html = await clipboardItem.getType(type).then((blob) => blob && blob.text());
break;
}
case FILE__BMP_CLIPBOARD_MIME_TYPE:
case FILE__JPEG_CLIPBOARD_MIME_TYPE:
case FILE__WEBP_CLIPBOARD_MIME_TYPE:
case FILE_PNG_CLIPBOARD_MIME_TYPE: {
const blob = await clipboardItem.getType(type);
const file = new File([blob], `pasted_image.${type.split('/')[1]}`, { type });
files.push(file);
break;
}
}
}
}
if (!html && !text && files.length) {
html = await this._createImagePasteHtml(files);
}

return this._genDocDataFromHtmlAndText(html, text);
} catch (e) {
Expand Down Expand Up @@ -479,30 +497,37 @@ export class DocClipboardService extends Disposable implements IDocClipboardServ
},
drawings: {},
};
const fileToBase64 = async (file: File): Promise<string> => {
const fileToBase64 = async (file: File): Promise<{ source: string; imageSourceType: ImageSourceType }> => {
const reader = new FileReader();
return new Promise((res) => {
reader.onloadend = function () {
res(reader.result as string);
res({
source: reader.result as string,
imageSourceType: ImageSourceType.BASE64,
});
};
reader.readAsDataURL(file);
});
};
const getImageSize = (base64: string): Promise<{ width: number; height: number }> => {
const getImageSize = (base64: string | File): Promise<{ width: number; height: number }> => {
const img = new Image();
const maxWidth = 500;
return new Promise((resolve) => {
img.src = base64;
img.src = typeof base64 === 'string' ? base64 : URL.createObjectURL(base64);
img.onload = () => {
const width = Math.min(maxWidth, img.naturalWidth);
const scale = img.naturalHeight / img.naturalWidth;
resolve({ width, height: width * scale });
};
});
};
// clipboardHooks 应该被重新设计,用来处理多个 hook 处理同一个节点的能力
// 参考 interceptor
const onBeforePasteImage = (this._clipboardHooks.find((e) => e.onBeforePasteImage)?.onBeforePasteImage!) ?? fileToBase64;

await Promise.all(files.map(async (file, index) => {
const base64 = await fileToBase64(file);
const { width = 100, height = 100 } = await getImageSize(base64);
const image = await onBeforePasteImage(file);
const { width = 100, height = 100 } = await getImageSize(file);
const itemId = `paste_image_id_${index}`;
const body = doc.body!;
const drawings = doc.drawings!;
Expand All @@ -512,17 +537,16 @@ export class DocClipboardService extends Disposable implements IDocClipboardServ
drawingId: itemId,
unitId: '',
subUnitId: '',
imageSourceType: ImageSourceType.BASE64,
imageSourceType: image.imageSourceType,
title: '',
source: base64,
source: image.source,
description: '',
layoutType: PositionedObjectLayoutType.INLINE,
drawingType: DrawingTypeEnum.DRAWING_IMAGE,
transform: {
width,
height,
angle: 0,

},
docTransform: {
angle: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,6 @@ export class HtmlToUDMService {
const docTransformHeight = Number(element.dataset.docTransformHeight || height);
// 外部会进行替换.
const id = Tools.generateRandomId(6);
body.textRuns!.push({
st: body.dataStream.length,
ed: body.dataStream.length,
});
doc.body?.customBlocks?.push({ startIndex: body.dataStream.length, blockId: id });
body.dataStream += '\b';
if (!doc.drawings) {
Expand Down
6 changes: 5 additions & 1 deletion packages/ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ export { DesktopBeforeCloseService, IBeforeCloseService } from './services/befor
export { CopyCommand, CutCommand, PasteCommand } from './services/clipboard/clipboard.command';
export {
BrowserClipboardService,
FILES_CLIPBOARD_MIME_TYPE,
FILE__BMP_CLIPBOARD_MIME_TYPE,
FILE__JPEG_CLIPBOARD_MIME_TYPE,
FILE__WEBP_CLIPBOARD_MIME_TYPE,
FILE_PNG_CLIPBOARD_MIME_TYPE,
FILE_SVG_XML_CLIPBOARD_MIME_TYPE,
HTML_CLIPBOARD_MIME_TYPE,
IClipboardInterfaceService,
PLAIN_TEXT_CLIPBOARD_MIME_TYPE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ import { supportClipboardAPI } from './clipboard-utils';

export const PLAIN_TEXT_CLIPBOARD_MIME_TYPE = 'text/plain';
export const HTML_CLIPBOARD_MIME_TYPE = 'text/html';
export const FILES_CLIPBOARD_MIME_TYPE = 'file';
export const FILE_PNG_CLIPBOARD_MIME_TYPE = 'image/png';
export const FILE__JPEG_CLIPBOARD_MIME_TYPE = 'image/jpeg';
export const FILE__BMP_CLIPBOARD_MIME_TYPE = 'image/bmp';
export const FILE__WEBP_CLIPBOARD_MIME_TYPE = 'image/webp';
export const FILE_SVG_XML_CLIPBOARD_MIME_TYPE = 'image/svg+xml';

/**
* This interface provides an interface to access system's clipboard.
Expand Down

0 comments on commit eb47fc5

Please sign in to comment.