Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

ClippingsToAnki

By: a guest on Feb 2nd, 2013  |  syntax: C#  |  size: 13.65 KB  |  views: 33  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Text;
  6. using System.Windows.Forms;
  7. using System.IO;
  8. using System.Text.RegularExpressions;
  9.  
  10. namespace ClippingsToAnki
  11. {
  12.         public partial class ClippingsToAnki : Form
  13.         {
  14.                 private static Regex RubyRegex = new Regex("《.*?》", RegexOptions.Compiled);
  15.                 private static Regex HTMLRubyRegex = new Regex("<rt>.*?</rt>", RegexOptions.Compiled);
  16.                 private static Regex HTMLRegex = new Regex("<.*?>", RegexOptions.Compiled);
  17.  
  18.                 public ClippingsToAnki()
  19.                 {
  20.                         InitializeComponent();
  21.  
  22.                         this.Icon = new System.Drawing.Icon(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("ClippingsToAnki.Cube.ico"));
  23.                        
  24.                         #if DEBUG
  25.                                 this.txtDebug.Visible = true;  
  26.                                 this.Height += 300;
  27.                         #endif
  28.                 }
  29.  
  30.                 private void InitializeForm()
  31.                 {
  32.                         this.cboBookName.Enabled = false;
  33.                         this.cboEncoding.SelectedIndex = 0;
  34.  
  35.                         string kindleDrive = this.GetKindleDrive();
  36.                         if(kindleDrive != string.Empty) {
  37.                                 this.txtClippingsFile.Text = Path.Combine(kindleDrive, "documents\\My Clippings.txt");
  38.                                 this.LoadBookList();
  39.                         } else {
  40.                                 MessageBox.Show("Your kindle doesn't appear to be plugged in.  Please manually select your \"My Clippings.txt\" file.", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
  41.                         }
  42.                 }
  43.  
  44.                 private string GetKindleDrive()
  45.                 {
  46.                         foreach(DriveInfo di in DriveInfo.GetDrives()) {
  47.                                 if(di.VolumeLabel.Equals("Kindle", StringComparison.InvariantCultureIgnoreCase) && di.DriveType == DriveType.Removable) {
  48.                                         return di.RootDirectory.FullName;
  49.                                 }
  50.                         }
  51.                        
  52.                         return string.Empty;
  53.                 }
  54.  
  55.                 private bool PopulateBookTitleDropdown()
  56.                 {
  57.                         try {
  58.                                 this.cboBookName.Items.Clear();
  59.  
  60.                                 foreach(Clipping clipping in this.GetClippings()) {
  61.                                         string bookName = clipping.BookTitle;
  62.  
  63.                                         if(!this.cboBookName.Items.Contains(bookName)) {
  64.                                                 this.cboBookName.Items.Add(bookName);
  65.                                         }
  66.                                 }
  67.  
  68.                                 this.cboBookName.SelectedIndex = 0;
  69.  
  70.                                 return true;
  71.                         } catch {
  72.                                 return false;
  73.                         }
  74.                 }
  75.  
  76.                 private List<Clipping> GetClippings()
  77.                 {
  78.                         string clippingText = File.ReadAllText(this.txtClippingsFile.Text).Replace("\xFEFF", "").Replace("\r", "");
  79.                         List<Clipping> clippings = new List<Clipping>();
  80.                         int filePos = 0;
  81.  
  82.                         foreach(string clippingContent in clippingText.Split(new string[] { "==========\n" }, StringSplitOptions.RemoveEmptyEntries)) {
  83.                                 Clipping clipping = new Clipping(clippingContent, filePos);
  84.  
  85.                                 if(clipping.Type == eClippingType.Highlight && clipping.Content.Trim() != string.Empty) {
  86.                                         clippings.Add(clipping);
  87.                                 }
  88.                                 filePos++;
  89.                         }
  90.                        
  91.                         clippings.Sort();
  92.  
  93.                         return clippings;
  94.                 }
  95.  
  96.                 private void txtClippingsFile_Leave(object sender, EventArgs e)
  97.                 {
  98.                         this.LoadBookList();
  99.                 }
  100.  
  101.                 private void LoadBookList()
  102.                 {
  103.                         this.UpdateGenerateButtonState();
  104.  
  105.                         if(!this.PopulateBookTitleDropdown()) {
  106.                                 MessageBox.Show("Could not read specified \"My Clippings.txt\" file (" + this.txtClippingsFile.Text + ")", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  107.                                 this.txtClippingsFile.Text = string.Empty;
  108.                                 this.cboBookName.Enabled = false;
  109.                         } else {
  110.                                 this.cboBookName.Enabled = true;
  111.                         }
  112.                 }
  113.  
  114.                 private string SelectFile(bool allowHTML)
  115.                 {
  116.                         OpenFileDialog ofd = new OpenFileDialog();
  117.  
  118.                         ofd.CheckFileExists = true;
  119.                         if(allowHTML) {
  120.                                 ofd.Filter = "Text and HTML Files|*.txt;*.html;*.htm";
  121.                         } else {
  122.                                 ofd.Filter = "Text Files|*.txt";
  123.                         }
  124.  
  125.                         if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
  126.                                 return ofd.FileName;
  127.                         }
  128.  
  129.                         return null;
  130.                 }
  131.  
  132.                 private void btnBrowseBook_Click(object sender, EventArgs e)
  133.                 {
  134.                         string fileName = this.SelectFile(true);
  135.  
  136.                         if(fileName != null) {
  137.                                 this.txtBookFile.Text = fileName;
  138.                                 this.UpdateGenerateButtonState();
  139.                         }
  140.                 }
  141.  
  142.                 private void btnBrowseClippingsFile_Click(object sender, EventArgs e)
  143.                 {
  144.                         string fileName = this.SelectFile(false);
  145.  
  146.                         if(fileName != null) {
  147.                                 this.txtClippingsFile.Text = fileName;
  148.                                 this.LoadBookList();
  149.                         }
  150.                 }
  151.  
  152.                 private bool GenerateHTML(Encoding bookEncoding, out string errorMessage, out string outputFilename)
  153.                 {
  154.                         errorMessage = string.Empty;
  155.                         outputFilename = string.Empty;
  156.  
  157.                         try {
  158.                                 //Strip aozora formatting
  159.                                 string bookText = RubyRegex.Replace(File.ReadAllText(this.txtBookFile.Text, bookEncoding), "").Replace("|", "");
  160.  
  161.                                 //Strip html formatting, convert </p> to new lines for end-of-sentence detection purposes
  162.                                 bookText = HTMLRegex.Replace(HTMLRubyRegex.Replace(bookText.Replace("</p>", "\n"), ""), "");
  163.  
  164.                                 string output = "<html><head><title>ClippingsToAnki Result</title>";
  165.                                 output +=       "<style type=\"text/css\">" +
  166.                                                                         "body {background:beige;color:#000;font-size:18px;}" +
  167.                                                                         ".hl {color:#F00;font-weight:bold;}" +
  168.                                                                         "a {color: #00aaff; text-decoration: none; font-weight:bold;}" +
  169.                                                                 "</style>";
  170.                                
  171.                                 output += "<body>";
  172.  
  173.                                 int currentTextIndex = 0;
  174.                                 int firstIndexForCurrentLocation = 0;
  175.                                 int lastLocation = -1;
  176.                                 int highlightCount = 0;
  177.                                 List<string> unmatchedWords = new List<string>();
  178.                                 foreach(Clipping clipping in this.GetClippings()) {
  179.                                         if(clipping.Type == eClippingType.Highlight && clipping.BookTitle == this.cboBookName.SelectedItem.ToString()) {
  180.                                                 highlightCount++;
  181.  
  182.                                                 if(lastLocation != clipping.Location) {
  183.                                                         firstIndexForCurrentLocation = clipping.Location;
  184.                                                 }
  185.                                                 currentTextIndex = bookText.IndexOf(clipping.Content, currentTextIndex);
  186.  
  187.                                                 if(currentTextIndex == -1) {
  188.                                                         //Try again from the start of the current location in case file order of highlights doesn't match text order
  189.                                                         currentTextIndex = bookText.IndexOf(clipping.Content, firstIndexForCurrentLocation);
  190.                                                        
  191.                                                         if(currentTextIndex == -1) {
  192.                                                                 //Still can't find the word, add it as a word that wasn't matched, reset index to start of current location
  193.                                                                 unmatchedWords.Add(clipping.Content);
  194.                                                                 currentTextIndex = firstIndexForCurrentLocation;
  195.                                                                 continue;
  196.                                                         }
  197.                                                 }
  198.  
  199.                                                 string sentence = this.GetSentence(bookText, currentTextIndex);
  200.  
  201.                                                 sentence = sentence.Insert(sentence.IndexOf(clipping.Content), "<span class=\"hl\">");
  202.                                                 sentence = sentence.Insert(sentence.IndexOf(clipping.Content)+clipping.Content.Length, "</span>");
  203.  
  204.                                                 string encodeSearchString = Uri.EscapeDataString("\"" + clipping.Content + "\"");
  205.                                                 output += "<a target=\"_blank\" href=\"https://www.google.co.jp/search?q=" + encodeSearchString + "&lr=lang_ja\">◎</a>&nbsp;&nbsp;";
  206.                                                 output += "<span style=\"display:none;\">。</span>";
  207.                                                 output += "<span>" + sentence + "</span><br/><br/>";
  208.                                         }
  209.                                 }
  210.  
  211.                                 output += "</body></html>";
  212.  
  213.                                 if(unmatchedWords.Count == highlightCount) {
  214.                                         errorMessage = "None of the words highlighted could be found in the specified book file.\n\nSelect the correct book/encoding and try again.";
  215.                                         return false;
  216.                                 } else if(unmatchedWords.Count > 0) {
  217.                                         errorMessage = "The following words/phrases were not found in the specified book file:";
  218.                                         foreach(string word in unmatchedWords) {
  219.                                                 errorMessage += word + Environment.NewLine;
  220.                                         }
  221.                                 }
  222.  
  223.                                 outputFilename = Path.Combine(Path.GetTempPath() + "clippingstoanki.html");
  224.                                 File.WriteAllText(outputFilename, output);
  225.                                 return true;
  226.                         } catch(Exception ex) {
  227.                                 errorMessage = ex.Message + "\n\n" + ex.StackTrace;
  228.                                 return false;
  229.                         }
  230.                 }
  231.  
  232.                 private void btnGenerate_Click(object sender, EventArgs e)
  233.                 {
  234.                         bool result;
  235.                         string message;
  236.                         string outputFilename;
  237.                         if(this.cboEncoding.SelectedIndex == 0) {
  238.                                 //Try Shift-JIS first, then UTF8 if Shift-JIS finds no word matches
  239.                                 result = this.GenerateHTML(Encoding.GetEncoding(932), out message, out outputFilename);
  240.                                 if(!result) {
  241.                                         result = this.GenerateHTML(Encoding.UTF8, out message, out outputFilename);
  242.                                 }
  243.                         } else {
  244.                                 Encoding bookEncoding;
  245.                                 if(this.cboEncoding.SelectedIndex == 1) {
  246.                                         bookEncoding = Encoding.GetEncoding(932);
  247.                                 } else {
  248.                                         bookEncoding = Encoding.UTF8;
  249.                                 }
  250.                                 result = this.GenerateHTML(Encoding.UTF8, out message, out outputFilename);
  251.                         }
  252.  
  253.                         if(message != string.Empty) {
  254.                                 if(!result) {
  255.                                         MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  256.                                 } else {
  257.                                         MessageBox.Show(message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
  258.                                 }
  259.                         }
  260.  
  261.                         if(result) {
  262.                                 System.Diagnostics.Process.Start(outputFilename);
  263.                         }
  264.                 }
  265.  
  266.                 private bool IsEndOfSentence(char chr)
  267.                 {
  268.                         return chr == '.' || chr == '。' || chr == '…' || chr == '\n' || chr == '\r';
  269.                 }
  270.  
  271.                 private string GetSentence(string text, int index)
  272.                 {
  273.                         int startPos = index;
  274.                         int endPos = index;
  275.                         int len = text.Length;
  276.  
  277.                         while(!this.IsEndOfSentence(text[startPos]) && startPos > 0) {
  278.                                 startPos--;
  279.                         }
  280.                         if(startPos != 0) {
  281.                                 startPos++;
  282.                         }
  283.  
  284.                         while(!this.IsEndOfSentence(text[endPos]) && endPos < len) {
  285.                                 endPos++;
  286.                         }
  287.  
  288.                         return text.Substring(startPos, endPos - startPos + 1).Trim();
  289.                 }
  290.  
  291.                 private void cboBookName_SelectedIndexChanged(object sender, EventArgs e)
  292.                 {
  293.                         this.UpdateGenerateButtonState();
  294.  
  295.                         #if DEBUG
  296.                                 this.txtDebug.Text = string.Empty;
  297.  
  298.                                 foreach(Clipping clipping in this.GetClippings()) {
  299.                                         if(clipping.BookTitle == this.cboBookName.SelectedItem.ToString()) {
  300.                                                 this.txtDebug.Text += clipping.ToString() + Environment.NewLine;
  301.                                         }
  302.                                 }
  303.                         #endif
  304.                 }
  305.  
  306.                 private void UpdateGenerateButtonState()
  307.                 {
  308.                         this.btnGenerate.Enabled = true;
  309.  
  310.                         if(this.cboBookName.SelectedIndex == -1) {
  311.                                 this.btnGenerate.Enabled = false;
  312.                                 return;
  313.                         }
  314.  
  315.                         if(!File.Exists(this.txtBookFile.Text)) {
  316.                                 this.btnGenerate.Enabled = false;
  317.                                 return;
  318.                         }
  319.  
  320.                         if(!File.Exists(this.txtClippingsFile.Text)) {
  321.                                 this.btnGenerate.Enabled = false;
  322.                                 return;
  323.                         }
  324.                 }
  325.  
  326.                 private void txtBookFile_Leave(object sender, EventArgs e)
  327.                 {
  328.                         this.UpdateGenerateButtonState();
  329.                 }
  330.                
  331.                 private void tlpMain_Click(object sender, EventArgs e)
  332.                 {
  333.                         this.UpdateGenerateButtonState();
  334.                 }
  335.  
  336.                 private void txtBookFile_Click(object sender, EventArgs e)
  337.                 {
  338.                         if(this.txtBookFile.Text.Trim() == string.Empty) {
  339.                                 this.btnBrowseBook_Click(null, null);
  340.                         }
  341.                 }
  342.  
  343.                 private void txtClippingsFile_Click(object sender, EventArgs e)
  344.                 {
  345.                         if(this.txtClippingsFile.Text.Trim() == string.Empty) {
  346.                                 this.btnBrowseClippingsFile_Click(null, null);
  347.                         }
  348.                 }
  349.  
  350.                 private void ClippingsToAnki_Shown(object sender, EventArgs e)
  351.                 {
  352.                         this.InitializeForm();
  353.                 }
  354.         }
  355.  
  356.         public enum eClippingType
  357.         {
  358.                 Unknown = 0,
  359.                 Highlight = 1,
  360.                 Note = 2
  361.         }
  362.  
  363.         public class Clipping : IComparable
  364.         {
  365.                 static Regex LocationRegex = new Regex("\\s([0-9]+)-{0,1}[0-9]*\\s\\|", RegexOptions.Compiled);
  366.                
  367.                 private string _firstLine = string.Empty;
  368.                 private string _secondLine = string.Empty;
  369.                 private string _remainingLines = string.Empty;
  370.                
  371.                 private int _filePos;
  372.  
  373.                 public Clipping(string clippingContent, int filePos)
  374.                 {
  375.                         string[] lines = clippingContent.Substring(0, clippingContent.Length).Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
  376.                         if(lines.Length > 2) {
  377.                                 this._firstLine = lines[0].Trim();
  378.                                 this._secondLine = lines[1].Trim();
  379.                                 for(int i = 2; i < lines.Length; i++) {
  380.                                         if(this._remainingLines != string.Empty) {
  381.                                                 this._remainingLines += "\n";
  382.                                         }
  383.                                         this._remainingLines += lines[i].Trim();
  384.                                 }
  385.                         }
  386.                         this._filePos = filePos;
  387.                 }
  388.  
  389.                 public string BookTitle
  390.                 {
  391.                         get
  392.                         {
  393.                                 //Return first line for Title + Author
  394.                                 return this._firstLine;
  395.                         }
  396.                 }
  397.  
  398.                 public int Location
  399.                 {
  400.                         get
  401.                         {
  402.                                 int location = -1;
  403.                                 string line = this._secondLine;
  404.                                
  405.                                 if(line.CountOf('|') > 1) {
  406.                                         //When both a page and a location are given, strip the page info
  407.                                         line = line.Substring(line.IndexOf("|"));
  408.                                 }
  409.  
  410.                                 Match match = LocationRegex.Match(line);
  411.                                 if(match.Groups.Count > 0) {
  412.                                         Int32.TryParse(match.Groups[1].Value, out location);
  413.                                 }
  414.                                 return location;
  415.                         }
  416.                 }
  417.  
  418.                 public eClippingType Type
  419.                 {
  420.                         get
  421.                         {
  422.                                 //En, Jap, Fr, Spanish, Chinese, German, Italian, Portuguese
  423.                                 if(this._secondLine.ContainsAny("Highlight", "ハイライト", "Surlignement", "Subrayado", "标注", "Markierung", "Evidenziazione", "Destaque")) {
  424.                                         return eClippingType.Highlight;
  425.                                 } else if(this._secondLine.ContainsAny("Note", "メモ", "Note", "Nota", "笔记", "Notiz", "Note", "Nota")) {
  426.                                         return eClippingType.Note;
  427.                                 }
  428.                                 return eClippingType.Unknown;
  429.                         }
  430.                 }
  431.  
  432.                 public string Content
  433.                 {
  434.                         get
  435.                         {
  436.                                 return this._remainingLines;
  437.                         }
  438.                 }
  439.  
  440.                 public int CompareTo(object obj)
  441.                 {
  442.                         if(obj is Clipping) {
  443.                                 Clipping b = (Clipping)obj;
  444.                                 if(this.Location < b.Location) {
  445.                                         return -1;
  446.                                 } else if(this.Location > b.Location) {
  447.                                         return 1;
  448.                                 } else {
  449.                                         if(this._filePos < b._filePos) {
  450.                                                 return -1;
  451.                                         } else {
  452.                                                 return 1;
  453.                                         }
  454.                                 }
  455.                         } else {
  456.                                 return 0;
  457.                         }
  458.                 }
  459.  
  460.                 public override string ToString()
  461.                 {
  462.                         return this.Type.ToString() + " - " + this.Location.ToString() + " - " + this.Content;
  463.                 }
  464.         }
  465.  
  466.         public static class StringExtension
  467.         {
  468.                 public static bool ContainsAny(this string str, params string[] needles)
  469.                 {
  470.                         string tmp = str.ToLowerInvariant();
  471.                         foreach(string needle in needles) {
  472.                                 if(tmp.Contains(needle.ToLowerInvariant())) {
  473.                                         return true;
  474.                                 }
  475.                         }
  476.  
  477.                         return false;
  478.                 }
  479.  
  480.                 public static int CountOf(this string str, char needle)
  481.                 {
  482.                         int count = 0;
  483.                         foreach(char c in str) {
  484.                                 if(c == needle) {
  485.                                         count++;
  486.                                 }
  487.                         }
  488.  
  489.                         return count;
  490.                 }
  491.         }
  492. }
clone this paste RAW Paste Data