Skip to content

Commit 517b76b

Browse files
committed
1 parent e4674ee commit 517b76b

File tree

3 files changed

+46
-4
lines changed

3 files changed

+46
-4
lines changed

src/common/context-menu.js

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,31 @@ export function createViewContextMenu(reader, params) {
9898
onCommand: () => {
9999
navigator.clipboard.writeText(params.overlay.url);
100100
}
101-
}
102-
],
103-
[
101+
},
104102
{
105103
label: reader._getString('general-copy'),
106104
disabled: !(params.overlay && params.overlay.type === 'math' && !reader.canCopy),
107105
onCommand: () => {
108106
navigator.clipboard.writeText(params.overlay.tex);
109107
}
110-
}
108+
},
109+
{
110+
label: reader._getString('reader-copy-image'),
111+
disabled: !(params.overlay && params.overlay.type === 'image' && !reader.canCopy
112+
&& typeof navigator.clipboard.write === 'function'),
113+
onCommand: async () => {
114+
// Browsers generally only support image/png in the Clipboard API,
115+
// so use a canvas to convert to PNG
116+
let bitmap = await createImageBitmap(params.overlay.image);
117+
let canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
118+
let ctx = canvas.getContext('bitmaprenderer');
119+
ctx.transferFromImageBitmap(bitmap);
120+
let blob = await canvas.convertToBlob({ type: 'image/png' });
121+
await navigator.clipboard.write([
122+
new ClipboardItem({ [blob.type]: blob })
123+
]);
124+
}
125+
},
111126
],
112127
[
113128
{
@@ -116,6 +131,21 @@ export function createViewContextMenu(reader, params) {
116131
onCommand: () => reader.copy()
117132
}
118133
],
134+
[
135+
(reader._platform === 'zotero' || window.dev) && {
136+
label: reader._getString('reader-save-image-as'),
137+
disabled: !(params.overlay && params.overlay.type === 'image'),
138+
onCommand: async () => {
139+
// onSaveImageAs() expects PNG
140+
let bitmap = await createImageBitmap(params.overlay.image);
141+
let canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
142+
let ctx = canvas.getContext('bitmaprenderer');
143+
ctx.transferFromImageBitmap(bitmap);
144+
let blob = await canvas.convertToBlob({ type: 'image/png' });
145+
reader._onSaveImageAs(blob);
146+
},
147+
}
148+
],
119149
[
120150
{
121151
label: reader._getString('reader-zoom-in'),

src/common/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,4 +192,8 @@ export type ViewContextMenuOverlay =
192192
| {
193193
type: 'math';
194194
tex: string;
195+
}
196+
| {
197+
type: 'image';
198+
image: ImageBitmapSource;
195199
};

src/dom/common/dom-view.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,6 +1267,14 @@ abstract class DOMView<State extends DOMViewState, Data> {
12671267
};
12681268
}
12691269

1270+
let img = el.closest('img');
1271+
if (img && /^(data|blob):/.test(img.src)) {
1272+
return {
1273+
type: 'image',
1274+
image: img,
1275+
};
1276+
}
1277+
12701278
return undefined;
12711279
}
12721280

0 commit comments

Comments
 (0)