Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/clipboard-manager-read-image-png.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"clipboard-manager": minor:feat
"clipboard-manager-js": minor:feat
---

Add `readImagePNG()` to read clipboard images as PNG bytes.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions plugins/clipboard-manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ tauri = { workspace = true, features = ["wry"] }

[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
arboard = { version = "3", features = ["wayland-data-control"] }
image = { version = "0.25", default-features = false, features = ["png"] }
2 changes: 2 additions & 0 deletions plugins/clipboard-manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,13 @@ Afterwards all the plugin's APIs are available through the JavaScript guest bind
import {
writeText,
readText,
readImagePNG,
writeHtml,
clear
} from '@tauri-apps/plugin-clipboard-manager'
await writeText('Tauri is awesome!')
assert(await readText(), 'Tauri is awesome!')
const pngBytes = await readImagePNG()
```

## Contributing
Expand Down
2 changes: 1 addition & 1 deletion plugins/clipboard-manager/api-iife.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions plugins/clipboard-manager/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const COMMANDS: &[&str] = &[
"read_text",
"write_image",
"read_image",
"read_image_png",
"write_html",
"clear",
];
Expand Down
35 changes: 34 additions & 1 deletion plugins/clipboard-manager/guest-js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,31 @@ async function readImage(): Promise<Image> {
)
}

/**
* Gets the clipboard image content as PNG bytes.
*
* #### Platform-specific
*
* - **Android / iOS:** Not supported.
*
* @example
* ```typescript
* import { readImagePNG } from '@tauri-apps/plugin-clipboard-manager';
*
* const clipboardImage = await readImagePNG();
* const blob = new Blob([clipboardImage], { type: 'image/png' })
* const url = URL.createObjectURL(blob)
* ```
* @since 2.4.0
*/
async function readImagePNG(): Promise<Uint8Array<ArrayBuffer>> {
const arr = await invoke<ArrayBuffer | number[]>(
'plugin:clipboard-manager|read_image_png'
)

return arr instanceof ArrayBuffer ? new Uint8Array(arr) : Uint8Array.from(arr)
}

/**
* * Writes HTML or fallbacks to write provided plain text to the clipboard.
*
Expand Down Expand Up @@ -148,4 +173,12 @@ async function clear(): Promise<void> {
await invoke('plugin:clipboard-manager|clear')
}

export { writeText, readText, writeHtml, clear, readImage, writeImage }
export {
writeText,
readText,
writeHtml,
clear,
readImage,
readImagePNG,
writeImage
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!

"$schema" = "../../schemas/schema.json"

[[permission]]
identifier = "allow-read-image-png"
description = "Enables the read_image_png command without any pre-configured scope."
commands.allow = ["read_image_png"]

[[permission]]
identifier = "deny-read-image-png"
description = "Denies the read_image_png command without any pre-configured scope."
commands.deny = ["read_image_png"]
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,32 @@ Denies the read_image command without any pre-configured scope.
<tr>
<td>

`clipboard-manager:allow-read-image-png`

</td>
<td>

Enables the read_image_png command without any pre-configured scope.

</td>
</tr>

<tr>
<td>

`clipboard-manager:deny-read-image-png`

</td>
<td>

Denies the read_image_png command without any pre-configured scope.

</td>
</tr>

<tr>
<td>

`clipboard-manager:allow-read-text`

</td>
Expand Down
12 changes: 12 additions & 0 deletions plugins/clipboard-manager/permissions/schemas/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,18 @@
"const": "deny-read-image",
"markdownDescription": "Denies the read_image command without any pre-configured scope."
},
{
"description": "Enables the read_image_png command without any pre-configured scope.",
"type": "string",
"const": "allow-read-image-png",
"markdownDescription": "Enables the read_image_png command without any pre-configured scope."
},
{
"description": "Denies the read_image_png command without any pre-configured scope.",
"type": "string",
"const": "deny-read-image-png",
"markdownDescription": "Denies the read_image_png command without any pre-configured scope."
},
{
"description": "Enables the read_text command without any pre-configured scope.",
"type": "string",
Expand Down
8 changes: 8 additions & 0 deletions plugins/clipboard-manager/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ pub(crate) async fn read_image<R: Runtime>(
Ok(rid)
}

#[command]
pub(crate) async fn read_image_png<R: Runtime>(
_app: AppHandle<R>,
clipboard: State<'_, Clipboard<R>>,
) -> Result<tauri::ipc::Response> {
clipboard.read_image_png().map(tauri::ipc::Response::new)
}

#[command]
pub(crate) async fn write_html<R: Runtime>(
_app: AppHandle<R>,
Expand Down
29 changes: 29 additions & 0 deletions plugins/clipboard-manager/src/desktop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: MIT

use arboard::ImageData;
use image::ImageEncoder;
use serde::de::DeserializeOwned;
use tauri::{image::Image, plugin::PluginApi, AppHandle, Runtime};

Expand Down Expand Up @@ -115,6 +116,34 @@ impl<R: Runtime> Clipboard<R> {
}
}

/// Warning: This method should not be used on the main thread! Otherwise the underlying libraries may deadlock on Linux, freezing the whole app, when trying to copy data copied from this app, for example if the user copies text from the WebView.
pub fn read_image_png(&self) -> crate::Result<Vec<u8>> {
match &self.clipboard {
Ok(clipboard) => {
let image = clipboard.lock().unwrap().as_mut().unwrap().get_image()?;
let expected_len = image
.width
.checked_mul(image.height)
.and_then(|len| len.checked_mul(4))
.ok_or_else(|| crate::Error::Clipboard("invalid image size".into()))?;

if image.bytes.len() != expected_len {
return Err(crate::Error::Clipboard("invalid image data length".into()));
}

let mut buffer = Vec::new();
image::codecs::png::PngEncoder::new(&mut buffer).write_image(
&image.bytes,
image.width as u32,
image.height as u32,
image::ExtendedColorType::Rgba8,
)?;
Ok(buffer)
}
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
}
}

pub(crate) fn cleanup(&self) {
if let Ok(clipboard) = &self.clipboard {
clipboard.lock().unwrap().take();
Expand Down
3 changes: 3 additions & 0 deletions plugins/clipboard-manager/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pub enum Error {
PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
#[error("{0}")]
Clipboard(String),
#[cfg(desktop)]
#[error("invalid image: {0}")]
Image(#[from] image::ImageError),
#[error(transparent)]
Tauri(#[from] tauri::Error),
}
Expand Down
1 change: 1 addition & 0 deletions plugins/clipboard-manager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
commands::write_text,
commands::read_text,
commands::read_image,
commands::read_image_png,
commands::write_image,
commands::write_html,
commands::clear
Expand Down
6 changes: 6 additions & 0 deletions plugins/clipboard-manager/src/mobile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ impl<R: Runtime> Clipboard<R> {
))
}

pub fn read_image_png(&self) -> crate::Result<Vec<u8>> {
Err(crate::Error::Clipboard(
"Unsupported on this platform".to_string(),
))
}

// Treat HTML as unsupported on mobile until tested
pub fn write_html<'a, T: Into<Cow<'a, str>>>(
&self,
Expand Down