{"id":3305,"date":"2020-03-20T21:24:13","date_gmt":"2020-03-20T19:24:13","guid":{"rendered":"https:\/\/blog.tomayac.com\/2020\/03\/20\/multi-mime-type-copying-with-the-async-clipboard-api\/"},"modified":"2020-03-20T21:24:13","modified_gmt":"2020-03-20T19:24:13","slug":"multi-mime-type-copying-with-the-async-clipboard-api","status":"publish","type":"post","link":"https:\/\/tomayac.com\/wordpress\/2020\/03\/20\/multi-mime-type-copying-with-the-async-clipboard-api\/","title":{"rendered":"Multi-MIME Type Copying with the Async Clipboard API"},"content":{"rendered":"\n\t\t\t<h2 id=\"copying-an-image\">Copying an Image <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2020\/03\/20\/multi-mime-type-copying-with-the-async-clipboard-api\/#copying-an-image\">\u2693<\/a><\/h2>\n<p>The <a href=\"https:\/\/w3c.github.io\/clipboard-apis\/#async-clipboard-api\">Asynchronous Clipboard API<\/a>\nprovides direct access to read and write clipboard data.\nApart from <em>text<\/em>, since Chrome\u00a076, you can also copy and paste <em>image<\/em> data with the API.\nFor more details on this, check out my\n<a href=\"https:\/\/web.dev\/image-support-for-async-clipboard\/\">article on web.dev<\/a>.\nHere's the gist of how copying an image blob works:<\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">copy<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token parameter\">blob<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=><\/span> <span class=\"token punctuation\">{<\/span><br>  <span class=\"token keyword\">try<\/span> <span class=\"token punctuation\">{<\/span><br>    <span class=\"token keyword\">await<\/span> navigator<span class=\"token punctuation\">.<\/span>clipboard<span class=\"token punctuation\">.<\/span><span class=\"token function\">write<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">[<\/span><br>      <span class=\"token keyword\">new<\/span> <span class=\"token class-name\">ClipboardItem<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">{<\/span><br>        <span class=\"token punctuation\">[<\/span>blob<span class=\"token punctuation\">.<\/span>type<span class=\"token punctuation\">]<\/span><span class=\"token operator\">:<\/span> blob<span class=\"token punctuation\">,<\/span><br>      <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span><br>    <span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>  <span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">catch<\/span> <span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>    console<span class=\"token punctuation\">.<\/span><span class=\"token function\">error<\/span><span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">.<\/span>name<span class=\"token punctuation\">,<\/span> err<span class=\"token punctuation\">.<\/span>message<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>  <span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<p>Note that you need to pass an array of <code>ClipboardItem<\/code>s to the <code>navigator.clipboard.write()<\/code> method,\nwhich implies that you can place more than one item on the clipboard\n(but this is not yet implemented in Chrome as of March\u00a02020).<\/p>\n<p>I have to admit, I only used to think of the clipboard as a one-item stack,\nso any new item replaces the existing one.\nHowever, for example, Microsoft Office\u00a0365's clipboard on Windows\u00a010 supports\n<a href=\"https:\/\/support.office.com\/en-us\/article\/copy-and-paste-using-the-office-clipboard-714a72af-1ad4-450f-8708-c2931e73ec8a#ID0EAABAAA=Windows\">up to 24\u00a0clipboard items<\/a>.<\/p>\n<h2 id=\"pasting-an-image\">Pasting an Image <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2020\/03\/20\/multi-mime-type-copying-with-the-async-clipboard-api\/#pasting-an-image\">\u2693<\/a><\/h2>\n<p>The generic code for pasting an image, that is, for reading from the clipboard,\nis a little more involved.\nAlso be advised that reading from the clipboard triggers a\n<a href=\"https:\/\/web.dev\/image-support-for-async-clipboard\/#security-permission\">permission prompt<\/a>\nbefore the read operation can succeed.\nHere's the trimmed down\n<a href=\"https:\/\/web.dev\/image-support-for-async-clipboard\/#paste-image\">example from my article<\/a>:<\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">paste<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=><\/span> <span class=\"token punctuation\">{<\/span><br>  <span class=\"token keyword\">try<\/span> <span class=\"token punctuation\">{<\/span><br>    <span class=\"token keyword\">const<\/span> clipboardItems <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> navigator<span class=\"token punctuation\">.<\/span>clipboard<span class=\"token punctuation\">.<\/span><span class=\"token function\">read<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>    <span class=\"token keyword\">for<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token keyword\">const<\/span> clipboardItem <span class=\"token keyword\">of<\/span> clipboardItems<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>      <span class=\"token keyword\">for<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token keyword\">const<\/span> type <span class=\"token keyword\">of<\/span> clipboardItem<span class=\"token punctuation\">.<\/span>types<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>        <span class=\"token keyword\">return<\/span> <span class=\"token keyword\">await<\/span> clipboardItem<span class=\"token punctuation\">.<\/span><span class=\"token function\">getType<\/span><span class=\"token punctuation\">(<\/span>type<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>      <span class=\"token punctuation\">}<\/span><br>    <span class=\"token punctuation\">}<\/span><br>  <span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">catch<\/span> <span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>    console<span class=\"token punctuation\">.<\/span><span class=\"token function\">error<\/span><span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">.<\/span>name<span class=\"token punctuation\">,<\/span> err<span class=\"token punctuation\">.<\/span>message<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>  <span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p>See how I first iterate over all <code>clipboardItems<\/code>\n(reminder, there can be just one in the current implementation),\nbut then also iterate over all <code>clipboardItem.types<\/code> of each individual <code>clipboardItem<\/code>,\nonly to then just stop at the first <code>type<\/code> and return whatever blob I encounter there.\nSo far I haven't really payed much attention to what this enables,\nbut yesterday, I had a sudden epiphany \ud83e\udd2f.<\/p>\n<h2 id=\"content-negotiation\">Content Negotiation <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2020\/03\/20\/multi-mime-type-copying-with-the-async-clipboard-api\/#content-negotiation\">\u2693<\/a><\/h2>\n<p>Before I get into the details of multi-MIME type copying,\nlet me quickly derail to\n<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Content_negotiation#Server-driven_content_negotiation\">server-driven content negotiation<\/a>, quoting straight from MDN:<\/p>\n<blockquote>\n<p>In server-driven content negotiation, or proactive content negotiation, the browser\n(or any other kind of user-agent) sends several HTTP headers along with the URL.\nThese headers describe the preferred choice of the user.\nThe server uses them as hints and an internal algorithm chooses the best content\nto serve to the client.<\/p>\n<\/blockquote>\n<p><img src=\"https:\/\/blog.tomayac.com\/images\/HTTPNegoServer.png\" alt=\"Server-driven content negotiation diagram\"><\/p>\n<h2 id=\"multi-mime-type-copying\">Multi-MIME Type Copying <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2020\/03\/20\/multi-mime-type-copying-with-the-async-clipboard-api\/#multi-mime-type-copying\">\u2693<\/a><\/h2>\n<p>A similar content negotiation mechanism takes place with copying.\nYou have probably encountered this effect before\nwhen you have copied rich text, like formatted HTML, into a plain text field:\nthe rich text is automatically converted to plain text.\n(\ud83d\udca1 Pro tip: to force pasting into a rich text context <em>without<\/em> formatting,\nuse <kbd>Ctrl<\/kbd> + <kbd>Shift<\/kbd> + <kbd>v<\/kbd> on Windows,\nor <kbd>Cmd<\/kbd> + <kbd>Shift<\/kbd> + <kbd>v<\/kbd> on macOS.)<\/p>\n<p>So back to content negotiation with image copying.\nIf you copy an SVG image, then open macOS\n<a href=\"https:\/\/support.apple.com\/guide\/preview\/welcome\/mac\">Preview<\/a>,\nand finally click &quot;File&quot;\u00a0&gt;\u00a0&quot;New from Clipboard&quot;,\nyou would probably expect an <em>image<\/em> to be pasted.\nHowever, if you copy an SVG image and paste it into\n<a href=\"https:\/\/code.visualstudio.com\/\">Visual Studio Code<\/a>\nor into <a href=\"https:\/\/jakearchibald.github.io\/svgomg\/\">SVGOMG<\/a>'s &quot;Paste markup&quot; field,\nyou would probably expect the <em>source code<\/em> to be pasted.<\/p>\n<p>With multi-MIME type copying, you can achieve exactly that \ud83c\udf89.\nBelow is the code of a future-proof <code>copy<\/code> function and some helper methods\nwith the following functionality:<\/p>\n<ul>\n<li>For images that are not SVGs, it creates a textual representation\nbased on the image's <code>alt<\/code> text attribute.\nFor SVG images, it creates a textual representation based on the SVG source code.<\/li>\n<li>At present, the Async Clipboard API only works with <code>image\/png<\/code>,\nbut nevertheless the code <em>tries<\/em> to put a representation in the image's original MIME type\ninto the clipboard, apart from a PNG representation.<\/li>\n<\/ul>\n<p>So in the generic case, for an SVG image, you would end up with three representations:\nthe source code as <code>text\/plain<\/code>, the SVG image as <code>image\/svg+xml<\/code>, and a PNG render as <code>image\/png<\/code>.<\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">copy<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token parameter\">img<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=><\/span> <span class=\"token punctuation\">{<\/span><br>  <span class=\"token comment\">\/\/ This assumes you have marked up images like so:<\/span><br>  <span class=\"token comment\">\/\/ &lt;img<\/span><br>  <span class=\"token comment\">\/\/    src=\"foo.svg\"<\/span><br>  <span class=\"token comment\">\/\/    data-mime-type=\"image\/svg+xml\"<\/span><br>  <span class=\"token comment\">\/\/    alt=\"Foo\"><\/span><br>  <span class=\"token comment\">\/\/<\/span><br>  <span class=\"token comment\">\/\/ Applying this markup could be automated<\/span><br>  <span class=\"token comment\">\/\/ (for all applicable MIME types):<\/span><br>  <span class=\"token comment\">\/\/<\/span><br>  <span class=\"token comment\">\/\/ document.querySelectorAll('img[src*=\".svg\"]')<\/span><br>  <span class=\"token comment\">\/\/ .forEach((img) => {<\/span><br>  <span class=\"token comment\">\/\/   img.dataset.mimeType = 'image\/svg+xml';<\/span><br>  <span class=\"token comment\">\/\/ });<\/span><br>  <span class=\"token keyword\">const<\/span> mimeType <span class=\"token operator\">=<\/span> img<span class=\"token punctuation\">.<\/span>dataset<span class=\"token punctuation\">.<\/span>mimeType<span class=\"token punctuation\">;<\/span><br>  <span class=\"token comment\">\/\/ Always create a textual representation based on the<\/span><br>  <span class=\"token comment\">\/\/ `alt` text, or based on the source code for SVG images.<\/span><br>  <span class=\"token keyword\">let<\/span> text <span class=\"token operator\">=<\/span> <span class=\"token keyword\">null<\/span><span class=\"token punctuation\">;<\/span><br>  <span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>mimeType <span class=\"token operator\">===<\/span> <span class=\"token string\">'image\/svg+xml'<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>    text <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> <span class=\"token function\">toSourceBlob<\/span><span class=\"token punctuation\">(<\/span>img<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>  <span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">else<\/span> <span class=\"token punctuation\">{<\/span><br>    text <span class=\"token operator\">=<\/span> <span class=\"token keyword\">new<\/span> <span class=\"token class-name\">Blob<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">[<\/span>img<span class=\"token punctuation\">.<\/span>alt<span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span>type<span class=\"token operator\">:<\/span> <span class=\"token string\">'text\/plain'<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><br>  <span class=\"token punctuation\">}<\/span><br>  <span class=\"token keyword\">const<\/span> clipboardData <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span><br>    <span class=\"token string\">'text\/plain'<\/span><span class=\"token operator\">:<\/span> text<span class=\"token punctuation\">,<\/span><br>  <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><br>  <span class=\"token comment\">\/\/ Always create a PNG representation.<\/span><br>  clipboardData<span class=\"token punctuation\">[<\/span><span class=\"token string\">'image\/png'<\/span><span class=\"token punctuation\">]<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> <span class=\"token function\">toPNGBlob<\/span><span class=\"token punctuation\">(<\/span>img<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>  <span class=\"token comment\">\/\/ When dealing with a non-PNG image, create a<\/span><br>  <span class=\"token comment\">\/\/ representation in the MIME type in question.<\/span><br>  <span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>mimeType <span class=\"token operator\">!==<\/span> <span class=\"token string\">'image\/png'<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>    clipboardData<span class=\"token punctuation\">[<\/span>mimeType<span class=\"token punctuation\">]<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> <span class=\"token function\">toOriginBlob<\/span><span class=\"token punctuation\">(<\/span>img<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>  <span class=\"token punctuation\">}<\/span><br>  <span class=\"token keyword\">try<\/span> <span class=\"token punctuation\">{<\/span><br>    <span class=\"token keyword\">await<\/span> navigator<span class=\"token punctuation\">.<\/span>clipboard<span class=\"token punctuation\">.<\/span><span class=\"token function\">write<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">[<\/span><br>      <span class=\"token keyword\">new<\/span> <span class=\"token class-name\">ClipboardItem<\/span><span class=\"token punctuation\">(<\/span>clipboardData<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span><br>    <span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>  <span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">catch<\/span> <span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>    <span class=\"token comment\">\/\/ Currently only `text\/plain` and `image\/png` are<\/span><br>    <span class=\"token comment\">\/\/ implemented, so if there is a `NotAllowedError`,<\/span><br>    <span class=\"token comment\">\/\/ remove the other representation.<\/span><br>    console<span class=\"token punctuation\">.<\/span><span class=\"token function\">warn<\/span><span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">.<\/span>name<span class=\"token punctuation\">,<\/span> err<span class=\"token punctuation\">.<\/span>message<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>    <span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">.<\/span>name <span class=\"token operator\">===<\/span> <span class=\"token string\">'NotAllowedError'<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>      <span class=\"token keyword\">const<\/span> disallowedMimeType <span class=\"token operator\">=<\/span> err<span class=\"token punctuation\">.<\/span>message<span class=\"token punctuation\">.<\/span><span class=\"token function\">replace<\/span><span class=\"token punctuation\">(<\/span><br>          <span class=\"token regex\">\/^.*?\\s(\\w+\\\/[^\\s]+).*?$\/<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">'$1'<\/span><span class=\"token punctuation\">)<\/span><br>      <span class=\"token keyword\">delete<\/span> clipboardData<span class=\"token punctuation\">[<\/span>disallowedMimeType<span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">;<\/span><br>      <span class=\"token keyword\">try<\/span> <span class=\"token punctuation\">{<\/span><br>        <span class=\"token keyword\">await<\/span> navigator<span class=\"token punctuation\">.<\/span>clipboard<span class=\"token punctuation\">.<\/span><span class=\"token function\">write<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">[<\/span><br>          <span class=\"token keyword\">new<\/span> <span class=\"token class-name\">ClipboardItem<\/span><span class=\"token punctuation\">(<\/span>clipboardData<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span><br>        <span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>      <span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">catch<\/span> <span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>        <span class=\"token keyword\">throw<\/span> err<span class=\"token punctuation\">;<\/span><br>      <span class=\"token punctuation\">}<\/span><br>    <span class=\"token punctuation\">}<\/span><br>  <span class=\"token punctuation\">}<\/span><br>  <span class=\"token comment\">\/\/ Log what's ultimately on the clipboard.<\/span><br>  console<span class=\"token punctuation\">.<\/span><span class=\"token function\">log<\/span><span class=\"token punctuation\">(<\/span>clipboardData<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><br><br><span class=\"token comment\">\/\/ Draws an image on an offscreen canvas<\/span><br><span class=\"token comment\">\/\/ and converts it to a PNG blob.<\/span><br><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">toPNGBlob<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token parameter\">img<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=><\/span> <span class=\"token punctuation\">{<\/span><br>  <span class=\"token keyword\">const<\/span> canvas <span class=\"token operator\">=<\/span> <span class=\"token keyword\">new<\/span> <span class=\"token class-name\">OffscreenCanvas<\/span><span class=\"token punctuation\">(<\/span><br>      img<span class=\"token punctuation\">.<\/span>naturalWidth<span class=\"token punctuation\">,<\/span> img<span class=\"token punctuation\">.<\/span>naturalHeight<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>  <span class=\"token keyword\">const<\/span> ctx <span class=\"token operator\">=<\/span> canvas<span class=\"token punctuation\">.<\/span><span class=\"token function\">getContext<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'2d'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>  <span class=\"token comment\">\/\/ This removes transparency. Remove at will.<\/span><br>  ctx<span class=\"token punctuation\">.<\/span>fillStyle <span class=\"token operator\">=<\/span> <span class=\"token string\">'#fff'<\/span><span class=\"token punctuation\">;<\/span><br>  ctx<span class=\"token punctuation\">.<\/span><span class=\"token function\">fillRect<\/span><span class=\"token punctuation\">(<\/span><span class=\"token number\">0<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">0<\/span><span class=\"token punctuation\">,<\/span> canvas<span class=\"token punctuation\">.<\/span>width<span class=\"token punctuation\">,<\/span> canvas<span class=\"token punctuation\">.<\/span>height<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>  ctx<span class=\"token punctuation\">.<\/span><span class=\"token function\">drawImage<\/span><span class=\"token punctuation\">(<\/span>img<span class=\"token punctuation\">,<\/span> <span class=\"token number\">0<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">0<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>  <span class=\"token keyword\">return<\/span> <span class=\"token keyword\">await<\/span> canvas<span class=\"token punctuation\">.<\/span><span class=\"token function\">convertToBlob<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><br><br><span class=\"token comment\">\/\/ Fetches an image resource and returns<\/span><br><span class=\"token comment\">\/\/ its blob of whatever MIME type.<\/span><br><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">toOriginBlob<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token parameter\">img<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=><\/span> <span class=\"token punctuation\">{<\/span><br>  <span class=\"token keyword\">const<\/span> response <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> <span class=\"token function\">fetch<\/span><span class=\"token punctuation\">(<\/span>img<span class=\"token punctuation\">.<\/span>src<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>  <span class=\"token keyword\">return<\/span> <span class=\"token keyword\">await<\/span> response<span class=\"token punctuation\">.<\/span><span class=\"token function\">blob<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><br><br><span class=\"token comment\">\/\/ Fetches an SVG image resource and returns<\/span><br><span class=\"token comment\">\/\/ a blob based on the source code.<\/span><br><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">toSourceBlob<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token parameter\">img<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=><\/span> <span class=\"token punctuation\">{<\/span><br>  <span class=\"token keyword\">const<\/span> response <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> <span class=\"token function\">fetch<\/span><span class=\"token punctuation\">(<\/span>img<span class=\"token punctuation\">.<\/span>src<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>  <span class=\"token keyword\">const<\/span> source <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> response<span class=\"token punctuation\">.<\/span><span class=\"token function\">text<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>  <span class=\"token keyword\">return<\/span> <span class=\"token keyword\">new<\/span> <span class=\"token class-name\">Blob<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">[<\/span>source<span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span>type<span class=\"token operator\">:<\/span> <span class=\"token string\">'text\/plain'<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<p>If you use this <code>copy<\/code> function (<a href=\"https:\/\/blog.tomayac.com\/2020\/03\/20\/multi-mime-type-copying-with-the-async-clipboard-api\/#demo\">demo<\/a> below \u2935\ufe0f) to copy an SVG image,\nfor example, everyone's favorite\n<a href=\"https:\/\/cdn.glitch.com\/8b0b00bb-4f86-41e8-b428-3d07b2b652a5%2FSymptoms_of_coronavirus_disease_2019_2.0.svg?v=1584617175181\">symptoms of coronavirus \ud83e\udda0 disease diagram<\/a>,\nand paste it in macOS Preview (that does not support SVG) or the &quot;Paste markup&quot; field of\nSVGOMG, this is what you get:<\/p>\n<figure>\n  <img src=\"https:\/\/blog.tomayac.com\/images\/preview.png\" alt=\"The macOS Preview app with a pasted PNG image.\" width=\"800\" height=\"623\">\n  <figcaption>\n    The macOS Preview app with a pasted PNG image.\n  <\/figcaption>\n<\/figure>\n<figure>\n  <img src=\"https:\/\/blog.tomayac.com\/images\/svgomg.png\" alt=\"The SVGOMG web app with a pasted SVG image.\" width=\"800\" height=\"539\">\n  <figcaption>\n    The SVGOMG web app with a pasted SVG image.\n  <\/figcaption>\n<\/figure>\n<h2 id=\"demo\">Demo <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2020\/03\/20\/multi-mime-type-copying-with-the-async-clipboard-api\/#demo\">\u2693<\/a><\/h2>\n<p><del>You can play with this code in the embedded example below.<\/del>\n<ins>Unfortunately you can't play with this code in the embedded example below yet,\nsince <a href=\"https:\/\/github.com\/w3c\/webappsec-feature-policy\/issues\/322\">webappsec-feature-policy#322<\/a>\nis still open.<\/ins>\nThe demo works if you <a href=\"https:\/\/async-clipboard-demo.glitch.me\/\">open it directly on Glitch<\/a>.<\/p>\n<div class=\"glitch-embed-wrap\" style=\"height: 600px; width: 100%; margin-block-end: 2rem;\">\n  <iframe src=\"https:\/\/glitch.com\/embed\/#!\/embed\/async-clipboard-demo?path=script.js&previewSize=100\" title=\"async-clipboard-demo on Glitch\" allow=\"clipboard-write; clipboard-read\" style=\"height: 100%; width: 100%; border: 0;\" loading=\"lazy\">\n  <\/iframe>\n<\/div>\n<h2 id=\"conclusion\">Conclusion <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2020\/03\/20\/multi-mime-type-copying-with-the-async-clipboard-api\/#conclusion\">\u2693<\/a><\/h2>\n<p>Programmatic multi-MIME type copying is a powerful feature.\nAt present, the Async Clipboard API is still limited,\nbut raw clipboard access is on the radar of the\n<a href=\"https:\/\/blog.tomayac.com\/2019\/09\/21\/project-fugu-at-w3c-tpac\/#breakout-session-for-a-more-capable-web%E2%80%94project-fugu\">\ud83d\udc21 Project Fugu team<\/a>\nthat I am a small part of.\nThe feature is being tracked as <a href=\"https:\/\/crbug.com\/897289\">crbug\/897289<\/a>.<\/p>\n<p>All that being said, raw clipboard access has its risks, too, as clearly pointed out in the\n<a href=\"https:\/\/github.com\/w3ctag\/design-reviews\/issues\/406\">TAG review<\/a>.\nI do hope use cases like multi-MIME type copying that I have motivated in this blog post\ncan help create developer enthusiasm so that browser engineers and security experts can make sure\nthe feature gets implemented and lands in a secure way.<\/p>\n\n\t\t\t<p>\n\t\t\t\t<img alt=\"Thomas Steiner\" width=\"32\" height=\"32\" src=\"https:\/\/blog.tomayac.com\/feed.php?dl=https%3A%2F%2Fblog.tomayac.com%2F2020%2F03%2F20%2Fmulti-mime-type-copying-with-the-async-clipboard-api%2F&dp=%2F2020%2F03%2F20%2Fmulti-mime-type-copying-with-the-async-clipboard-api%2F&dt=Multi-MIME%20Type%20Copying%20with%20the%20Async%20Clipboard%20API\" alt=\"\">\n\t\t\t\t<br\/>This post appeared first on <a href=\"https:\/\/blog.tomayac.com\/2020\/03\/20\/multi-mime-type-copying-with-the-async-clipboard-api\/\">https:\/\/blog.tomayac.com\/2020\/03\/20\/multi-mime-type-copying-with-the-async-clipboard-api\/<\/a>.\n\t\t\t<\/p>\n\t\t","protected":false},"excerpt":{"rendered":"<p>\t\t\tCopying an Image \u2693<br \/>\nThe Asynchronous Clipboard API<br \/>\nprovides direct access to read and write clipboard data.<br \/>\nApart from text, since Chrome\u00a076, you can also copy and paste image data with the API.<br \/>\nFor more details on this, check out my<br \/>\narticle on web&#8230;.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"intlwemo_wallet_address":"","footnotes":""},"categories":[],"tags":[],"class_list":["post-3305","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/posts\/3305","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/comments?post=3305"}],"version-history":[{"count":8,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/posts\/3305\/revisions"}],"predecessor-version":[{"id":3392,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/posts\/3305\/revisions\/3392"}],"wp:attachment":[{"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/media?parent=3305"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/categories?post=3305"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/tags?post=3305"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}