Commits

geekman committed b44b54d Draft

Initial commit.

  • Participants

Comments (0)

Files changed (11)

+Copyright (c) 2012, Darell Tan
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+Export Drawings Chrome Extension
+==================================
+This Chrome extension allows you to easily export drawings in a Google Doc as PDFs.
+
+You can export all images (including drawings) and the Google Doc itself by selecting "File > Download as... > Web Page (.html, zipped)", which gives you a ZIP file containing a HTML document as well as the images, exported as PNG or JPEG, depending on the original image format. The images are numbered in the order of their appearance in the document, starting from 0, and will be named like `image00.png`. However, drawings in the Google Doc will be exported as PNGs to maximize compatibility with all web browsers, since the HTML document references those images. 
+
+This extension allows you to easily export all those drawings in a vector format to replace those rasterized images.
+
+
+Usage Instructions
+--------------------
+After you install this extension, an icon will appear in the omnibar when you are viewing a Google Doc. Clicking this icon will scan the document for both images and drawings, and display them in a popup.
+
+![Screenshot](https://bitbucket.org/geekman/export-gdoc-drawings-crx/raw/tip/screenshot.png)
+
+All the images will be named according to the naming scheme described earlier. The extension only allows drawings to be selected. If you need to download the other images, download the document as a zipped Web Page instead.
+
+Click the "Download" button to download the selected drawings.
+
+
+Known Issues
+--------------
+Google Chrome will warn you that the files "can harm your computer". You will need to manually select "Keep" for all of the files. Since this is a browser security feature, there is no way to programmatically override it.
+
+Note that long documents are lazily loaded, meaning they will initially download and show only the first page of the document - the remaining pages have not yet been loaded. Due to this behaviour, the extension may not correctly detect all the images contained in the Google Doc. You should _slowly_ page through the document to trigger each page to be downloaded, then click on the extension icon again. If you do not do this, the images may be numbered out of sequence and may not correspond to the filenames in the exported ZIP file.
+
+Google Doc drawings, when exported as PDFs, will contain the entire drawing canvas including the empty space that is not occupied by the drawing. Google automatically removes this empty space when the drawings are displayed as PNG, whether in the online view or exported in the ZIP file. You will therefore have to manually remove this empty space.
+
+
+LICENSE
+--------
+export-gdoc-drawings-crx is licensed under the 3-clause ("modified") BSD License.
+
+Copyright (C) 2012 Darell Tan
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// show our pageAction when the tab is a Google Doc
+chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
+	if (tab.url.indexOf('https://docs.google.com/document/') == 0) {
+		chrome.pageAction.show(tabId);
+	}
+});
+
+
+function getDrawings(req, respFunc) {
+	var drawings = [];
+	var imgs = document.querySelectorAll('.kix-lineview-content img');
+	for (var i = 0; i < imgs.length; i++) 
+		drawings.push(imgs[i].src);
+
+	respFunc(drawings);
+};
+
+function download(req, respFunc) {
+	var link = document.createElement('a');
+	link.href = req.url;
+	link.download = req.filename;
+	link.click();
+}
+
+function downloadBlob(req, respFunc) {
+	var xhr = new XMLHttpRequest();
+	xhr.open('GET', req.url, true);
+	//xhr.filename = req.filename;	// not necessary here
+	xhr.responseType = 'blob';
+	
+	xhr.onload = function(e) {
+		var objUrl = window.webkitURL.createObjectURL(xhr.response);
+
+		var link = document.createElement('a');
+		link.href = objUrl;
+		link.download = req.filename;
+		link.click();
+
+		// remove the object url after use
+		window.addEventListener("unload", function() {
+			window.webkitURL.revokeObjectURL(objUrl);
+		}, false);
+
+		// report status back to caller, if required
+		if (respFunc) 
+			respFunc({status: 'complete', url: req.url});
+	};
+	
+	xhr.onerror = function(e) {
+		// report status back to caller, if required
+		if (respFunc) 
+			respFunc({status: 'error', url: req.url});
+	};
+
+	// begin request
+	xhr.send();
+
+	if (respFunc) 
+		respFunc({status: 'loadstart', url: req.url});
+}
+
+// scrolls to the <img> with the specified src url
+function scrollToImg(req, resp) {
+	var imgSrc = req.imgSrc;
+	var alignTop = req.alignTop || false;
+
+	var imgs = document.querySelectorAll('.kix-lineview-content img');
+	for (var i = 0; i < imgs.length; i++) {
+		if (imgs[i].src == imgSrc) {
+			imgs[i].scrollIntoView(alignTop);
+			break;
+		}
+	}
+}
+
+// listener for content script to perform functions
+chrome.extension.onMessage.addListener(function(req, sender, respFunc) {
+	actions = {
+		'list': getDrawings,
+		'download': download,
+		'downloadBlob': downloadBlob,
+		'scrollToImg': scrollToImg,
+	};
+
+	if (req.action in actions)
+		actions[req.action](req, respFunc);
+});
+

icons/icon128.png

Added
New image

icons/icon16.png

Added
New image
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="267.52289"
+   height="228"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.2 r9819"
+   sodipodi:docname="icons.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.98994952"
+     inkscape:cx="124.93211"
+     inkscape:cy="116.11166"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1175"
+     inkscape:window-height="658"
+     inkscape:window-x="392"
+     inkscape:window-y="183"
+     inkscape:window-maximized="0"
+     fit-margin-top="50"
+     fit-margin-left="50"
+     fit-margin-right="50"
+     fit-margin-bottom="50" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-72.85714,-314.36218)">
+    <g
+       id="icon128"
+       inkscape:label="#g3790"
+       transform="translate(-12.12183,-4.7613928)">
+      <rect
+         inkscape:label="#rect2987"
+         ry="10.83871"
+         y="364.36218"
+         x="122.85714"
+         height="128"
+         width="128"
+         id="bgrect"
+         style="fill:#676767;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+      <path
+         inkscape:label="#path2997"
+         sodipodi:nodetypes="cccccccc"
+         inkscape:connector-curvature="0"
+         id="mountains"
+         d="m 198.52617,415.24669 38.13097,62.22849 -49.40357,0 -26.87921,0 -22.3785,0 20.24935,-36.53261 15.56875,14.61313 z"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+    </g>
+    <g
+       id="icon16"
+       transform="matrix(0.125,0,0,0.125,271.89251,426.05552)"
+       inkscape:label="#g3805">
+      <rect
+         style="fill:#676767;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         id="rect3807"
+         width="128"
+         height="128"
+         x="122.85714"
+         y="364.36218"
+         ry="0"
+         inkscape:label="#rect2987" />
+      <path
+         style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         d="m 198.52617,415.24669 38.13097,62.22849 -49.40357,0 -26.87921,0 -22.3785,0 20.24935,-36.53261 15.56875,14.61313 z"
+         id="path3809"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="cccccccc"
+         inkscape:label="#path2997" />
+    </g>
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+       x="146.45406"
+       y="525.02271"
+       id="text3041"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3043"
+         x="146.45406"
+         y="525.02271">128px</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+       x="272.53482"
+       y="525.02271"
+       id="text3045"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3047"
+         x="272.53482"
+         y="525.02271">16px</tspan></text>
+  </g>
+</svg>
+{
+	"name": "Export Google Doc Drawings",
+		"version": "1.0",
+		"manifest_version": 2,
+		"description": "Exports drawings on a Google Doc to PDF files. It behaves similar to when you use export a Google Doc as \"Web Page (zipped)\", but instead uses PDFs for drawings.",
+		"icons": {
+			"16":  "icons/icon16.png",
+			"128": "icons/icon128.png"
+		},
+
+		"background": {
+			"scripts": ["background.js"] 
+		},
+		"content_scripts": [
+			{ "matches": ["https://docs.google.com/document/*"], 
+				"js": ["content.js"] }
+		],
+		"page_action": {
+			"default_icon":  "icons/icon16.png",
+			"default_title": "Export Drawings",
+			"default_popup": "popup.html"
+		},
+		"permissions": [
+			"tabs"
+		]
+}
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Export Google Drawings</title>
+<script type="text/javascript" src="popup.js"></script>
+<style type="text/css">
+	body, button {
+		font-family: 'Segoe UI', Tahoma, sans-serif; 
+	}
+
+	body {
+		font-size: 75%;
+		width: 30em;
+	}
+	
+	button {
+		padding: 0.2em 0.8em;
+	}
+
+	a.goto {
+		font-size: 65%;
+		margin-left: 0.5em;
+	}
+
+	.disabled label {
+		color: gray;
+	}
+
+	div#drawings {
+		margin-bottom: 1em;
+	}
+
+	div.no_items {
+		color: gray;
+		font-weight: bold;
+		margin: 2em;
+	}
+
+	.item.header {
+		background-color: #EBEFF9;
+		border-bottom: 1px solid #9CC2EF;
+		margin: 0.2em 0;
+		padding: 0.2em 0;
+	}
+</style>
+</head>
+
+<body>
+	<p style="color:gray">Hint: You may need to scroll all the way to the end of the Google Document to cause all images to be loaded</p>
+
+	<p>Check the drawings to be exported, then click the <b>Download</b> button.</p>
+
+	<div id="drawings"></div>
+
+	<button id="download">Download</button>
+</body>
+</html>
+
+
+// sends a message to the currently-selected tab
+function sendMessage(req, resp) {
+	chrome.tabs.getSelected(null, function(tab) {
+		chrome.tabs.sendMessage(tab.id, req, resp);
+	});
+}
+
+function zeroPad(x, digits) {
+	var strx = x.toString();
+	var len = strx.length;
+	for (var i = digits - len; i > 0; i--) 
+		strx = '0' + strx;
+	return strx;
+}
+
+// valid parts are: hash, host, hostname, href, pathname, port, protocol, search
+function getUrlPart(url, part) {
+	var a = document.createElement('a');
+	a.href = url;
+	return a[part];
+}
+
+function $foreach(selector, func) {
+	var items = document.querySelectorAll(selector);
+	for (var i = 0; i < items.length; i++) 
+		func.call(items[i], i);
+}
+
+// extracts the drawing ID from the given URL
+function getDrawingId(url) {
+	var params = getUrlPart(url, 'search');
+	var idMatch = params.match('[?&]id=([^&]+)');
+	return idMatch ? idMatch[1] : null;
+}
+
+// constructs a drawing export URL for the given drawing and format
+function getDrawingExportUrl(url, format) {
+	var id = getDrawingId(url);
+	if (id && ['pdf', 'svg'].indexOf(format) != -1)
+		return 'https://docs.google.com/drawings/d/' + id + '/export/' + format + '?id=' + id + '&pageid=p';
+	return null;
+}
+
+/////////////////////////////////////////////////////////////////////
+
+function downloadDrawings() {
+	$foreach('input[type=checkbox].download', function() {
+		if (this.checked) {
+			sendMessage({action: 'downloadBlob', url: this.value, filename: this.id}, null);
+			console.log("download", this.value, this.id);
+		}
+	});
+}
+
+function scrollToImg() {
+	sendMessage({action: 'scrollToImg', imgSrc: this.href, alignTop: true}, null);
+}
+
+function toggleSelectAll() {
+	var allCheckbox = this;
+
+	$foreach('input[type=checkbox].download', function() {
+		if (!this.disabled)
+			this.checked = allCheckbox.checked;
+	});
+}
+
+function getDrawings() {
+	sendMessage({action: 'list'}, function(drawings) {
+		var numDigits = drawings.length.toString().length;
+
+		var d = document.getElementById('drawings');
+		d.innerHTML = '';
+
+		if (drawings.length == 0) {
+			d.innerHTML = '<div class="no_items">No images</div>';
+		} else {
+			// emit top header
+			d.innerHTML = '<div class="item header">' + 
+						'<input type="checkbox" id="_all"/>' + 
+						'<label for="_all">all images</label>' +
+						'</div>';
+		}
+
+		for (var i = 0; i < drawings.length; i++) {
+			url = drawings[i];
+			var imgId = 'image' + zeroPad(i, numDigits);
+			var disabled = false;
+			var downloadUrl = url;
+			var desc = imgId;
+
+			if (url.match("://[^.]+\.googleusercontent\.com/")) {
+				//desc = 'image (' + getUrlPart(url, 'pathname') + ')';
+				disabled = true;
+			} else if (url.indexOf("://docs.google.com/drawings/") > -1) {
+				//desc = 'drawing (' + getDrawingId(url) + ')';
+				desc += ' (drawing)';
+				downloadUrl = getDrawingExportUrl(url, 'pdf');
+			}
+
+			itemHtml = '';
+			itemHtml += '<div class="item ' + (disabled ? 'disabled' : '') + '">';
+			itemHtml += '<input type="checkbox" class="download" id="' + imgId + '" value="' + downloadUrl + '" ' + (disabled ? 'disabled="disabled"' : '') + '/>';
+			itemHtml += '<label for="' + imgId + '">' + desc + '</label>';
+			itemHtml += '<a class="goto" href="' + url + '">go to</a>';
+			itemHtml += '</div>';
+
+			d.innerHTML += itemHtml;
+		}
+
+		// the select-all checkboxes
+		$foreach('input[type=checkbox]#_all', function() {
+			this.addEventListener("click", toggleSelectAll, false);
+
+			// select all checkboxes initially
+			this.click();
+		});
+
+		// goto links
+		$foreach('a.goto', function() {
+			this.addEventListener("click", scrollToImg, false);
+		});
+
+		// download button
+		$foreach('button#download', function() {
+			this.addEventListener("click", downloadDrawings, false);
+		});
+	});
+}
+
+document.addEventListener("DOMContentLoaded", function() {
+	getDrawings();
+}, false);
+

screenshot.png

Added
New image