 		return plist
 	}
 }
+
+private extension ArticlePasteboardWriter {
+
+	func plainText() -> String {
+
+		var s = ""
+
+		if let title = article.title {
+			s += "\(title)\n\n"
+		}
+		if let text = article.contentText {
+			s += "\(text)\n\n"
+		}
+		else if let summary = article.summary {
+			s += "\(summary)\n\n"
+		}
+		else if let html = article.contentHTML {
+			let convertedHTML = html.rs_stringByConvertingToPlainText()
+			s += "\(convertedHTML)\n\n"
+		}
+
+		if let url = article.url {
+			s += "URL: \(url)\n\n"
+		}
+		if let externalURL = article.externalURL {
+			s += "external URL: \(externalURL)\n\n"
+		}
+
+		s += "Date: \(article.logicalDatePublished)\n\n"
+
+		if let feed = article.feed {
+			s += "Feed: \(feed.nameForDisplay)\n"
+			if let homePageURL = feed.homePageURL {
+				s += "Home page: \(homePageURL)\n"
+			}
+			s += "URL: \(feed.url)"
+		}
+
+		return s
+	}
+
+	private struct Key {
+
+		static let articleID = "articleID" // database ID, unique per account
+		static let uniqueID = "uniqueID" // unique ID, unique per feed (guid, or possibly calculated)
+		static let feedURL = "feedURL"
+		static let feedID = "feedID" // may differ from feedURL if coming from a syncing system
+		static let title = "title"
+		static let contentHTML = "contentHTML"
+		static let contentText = "contentText"
+		static let url = "url" // usually a permalink
+		static let externalURL = "externalURL" // usually not a permalink
+		static let summary = "summary"
+		static let imageURL = "imageURL"
+		static let bannerImageURL = "bannerImageURL"
+		static let datePublished = "datePublished"
+		static let dateModified = "dateModified"
+		static let dateArrived = "dateArrived"
+		static let read = "read"
+		static let starred = "starred"
+		static let userDeleted = "userDeleted"
+		static let authors = "authors"
+		static let attachments = "attachments"
+
+		// Author
+		static let authorName = "name"
+		static let authorURL = "url"
+		static let authorAvatarURL = "avatarURL"
+		static let authorEmailAddress = "emailAddress"
+
+		// Attachment
+		static let attachmentURL = "url"
+		static let attachmentMimeType = "mimeType"
+		static let attachmentTitle = "title"
+		static let attachmentSizeInBytes = "sizeInBytes"
+		static let attachmentDurationInSeconds = "durationInSeconds"
+
+		// Internal
+		static let accountID = "accountID"
+	}
+
+	func exportDictionary() -> [String: Any] {
+
+		var d = [String: Any]()
+
+		d[Key.articleID] = article.articleID
+		d[Key.uniqueID] = article.uniqueID
+
+		if let feed = article.feed {
+			d[Key.feedURL] = feed.url
+		}
+
+		d[Key.feedID] = article.feedID
+		d[Key.title] = article.title ?? nil
+		d[Key.contentHTML] = article.contentHTML ?? nil
+		d[Key.contentText] = article.contentText ?? nil
+		d[Key.url] = article.url ?? nil
+		d[Key.externalURL] = article.externalURL ?? nil
+		d[Key.summary] = article.summary ?? nil
+		d[Key.imageURL] = article.imageURL ?? nil
+		d[Key.bannerImageURL] = article.bannerImageURL ?? nil
+		d[Key.datePublished] = article.datePublished ?? nil
+		d[Key.dateModified] = article.dateModified ?? nil
+		d[Key.dateArrived] = article.status.dateArrived
+		d[Key.authors] = authorDictionaries() ?? nil
+		d[Key.attachments] = attachmentDictionaries() ?? nil
+
+		return d
+	}
+
+	func internalDictionary() -> [String: Any] {
+
+		var d = exportDictionary()
+		d[Key.accountID] = article.accountID
+		return d
+	}
+
+	func authorDictionary(_ author: Author) -> [String: Any] {
+
+		var d = [String: Any]()
+
+		d[Key.authorName] = author.name ?? nil
+		d[Key.authorURL] = author.url ?? nil
+		d[Key.authorAvatarURL] = author.avatarURL ?? nil
+		d[Key.authorEmailAddress] = author.emailAddress ?? nil
+
+		return d
+
+	}
+
+	func attachmentDictionary(_ attachment: Attachment) -> [String: Any] {
+
+		var d = [String: Any]()
+
+		d[Key.attachmentURL] = attachment.url
+		d[Key.attachmentMimeType] = attachment.mimeType ?? nil
+		d[Key.attachmentTitle] = attachment.title ?? nil
+		d[Key.attachmentSizeInBytes] = attachment.sizeInBytes ?? nil
+		d[Key.attachmentDurationInSeconds] = attachment.durationInSeconds ?? nil
+
+		return d
+	}
+
+	func authorDictionaries() -> [[String: Any]]? {
+
+		guard let authors = article.authors, !authors.isEmpty else {
+			return nil
+		}
+		return authors.map{ authorDictionary($0) }
+	}
+
+	func attachmentDictionaries() -> [[String: Any]]? {
+
+		guard let attachments = article.attachments, !attachments.isEmpty else {
+			return nil
+		}
+		return attachments.map { attachmentDictionary($0) }
+	}
+}
+


	func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] {

		// TODO: add more types

		var types = [NSPasteboard.PasteboardType]()

		if let _ = article.title {
			types += [.string]
		}
		if let link = article.preferredLink, let _ = URL(string: link) {
			types += [.URL]
		}

		return types // TODO: add types
	}

	func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? {

		// TODO: write data for all types declared in writableTypes.

		let plist: Any?

		switch type {
		case .string:
			plist = article.title ?? ""
		case .URL:
			plist = article.preferredLink ?? ""
		default:
			plist = nil
		}

@objc final class ArticlePasteboardWriter: NSObject, NSPasteboardWriting {

	private let article: Article
	static let articleUTI = "com.ranchero.article"
	static let articleUTIType = NSPasteboard.PasteboardType(rawValue: articleUTI)
	static let articleUTIInternal = "com.ranchero.evergreen.internal.article"
	static let articleUTIInternalType = NSPasteboard.PasteboardType(rawValue: articleUTIInternal)

	private lazy var renderedHTML: String = {
		let articleRenderer = ArticleRenderer(article: article, style: ArticleStylesManager.shared.currentStyle)
		return articleRenderer.html
	}()

	init(article: Article) {

