diff --git a/TBO/Program.cs b/TBO/Program.cs index 373c2b0..e1a4a4f 100644 --- a/TBO/Program.cs +++ b/TBO/Program.cs @@ -1,8 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Windows.Forms; +using Microsoft.Win32; +using System; +using System.Reflection; using TBO.UI; +using XWolf; namespace TBO { @@ -14,8 +14,11 @@ [STAThread] static void Main() { + //ShellNotification.NotifyOfChange(); + //if (!FileAssociation.IsAssociatedWithMe("tbo")) + // FileAssociation.AssociateMe("tbo", "XWolfTebeo", "Tebeo", 0); + //AssociateMe("tbo", "XWolfTebeo", "Tebeo", 0,null); UIManager.Manager.EnterStudio(); - } } } diff --git a/TBO/TBO.csproj b/TBO/TBO.csproj index 9097062..3e8159d 100644 --- a/TBO/TBO.csproj +++ b/TBO/TBO.csproj @@ -47,6 +47,9 @@ + + FileAssociationNT6.cs + xIO2.cs diff --git a/TBO/TBOFile.cs b/TBO/TBOFile.cs index 621632f..40b81df 100644 --- a/TBO/TBOFile.cs +++ b/TBO/TBOFile.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Drawing; +using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; using System.Text; +using TBO.Properties; using XWolf.IO; namespace TBO @@ -13,15 +15,18 @@ private const string FILEHEADER = "TBO"; private const int EOF = 0x1A; private const int FILEVERSION = 0; + private const uint ENDIANMARK = 0xFEEDFACE; private static ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg); private static EncoderParameters eParams = new EncoderParameters(1); private FileStream fs; + private long tboStart; private bool readOnly; - private bool empty; + private bool changed; private long metapos; - private long[] pagepos; + private bool sameEndian; + private List pagepos = new List(); static TBOFile() { @@ -33,6 +38,7 @@ try { fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite); + readOnly = false; } catch { @@ -41,16 +47,116 @@ fs = new FileStream(filePath, FileMode.Open, FileAccess.Read); readOnly = true; } - catch { throw new Exception("No se pudo abrir ni crear el archivo"); } + catch { throw new Exception("No se pudo abrir ni crear el tebeo"); } } if (fs.Length == 0) WriteHeader(); else - ReadHeader(); + ReadHeader(false); ReadDirectory(); ReadMetadata(); } + public void Dispose() + { + if (changed) + Commit(); + fs.Close(); + } + + #region I/O + + private void ReadHeader(bool hard = true) + { + fs.Position = tboStart; + if (Encoding.ASCII.GetString(fs.Read(FILEHEADER.Length)) != FILEHEADER) + if (hard) + throw new Exception("El arhivo no es un tebeo"); + else + { + // Search enveloped TBO + fs.Position = fs.Length - FILEHEADER.Length; + if (Encoding.ASCII.GetString(fs.Read(FILEHEADER.Length)) != FILEHEADER) + throw new Exception("El arhivo no contiene un tebeo"); + fs.Position = fs.Length - (FILEHEADER.Length + sizeof(int)); + tboStart = fs.ReadInt32(); + ReadHeader(); + return; + } + if (fs.ReadByte() != EOF) + throw new Exception("Error al leer el tebeo"); + if (fs.ReadByte() != FILEVERSION) + throw new Exception("No se puede leer esta versión del tebeo"); + sameEndian = fs.ReadUInt32() == ENDIANMARK; + } + + private void WriteHeader() + { + fs.Position = tboStart; + fs.Write(Encoding.ASCII.GetBytes(FILEHEADER)); + fs.WriteByte(EOF); + fs.WriteByte(FILEVERSION); + fs.WriteUInt32(ENDIANMARK); + } + + private void ReadDirectory() + { + pagepos.Clear(); + if (fs.Position >= fs.Length) + { + metapos = fs.Position + sizeof(int); + } + else + { + long pos = fs.Position; + int lng; + while ((lng = fs.ReadInt32()) > 0) + { + pagepos.Add(pos); + fs.Position = pos = fs.Position + lng; + } + metapos = fs.Position; + } + } + + private void ReadMetadata() + { + fs.Position = metapos; + if (fs.Position >= fs.Length) + return; + fs.DeserializeTo(this); + } + + private Image ReadImage() + { + MemoryStream ms = new MemoryStream(fs.ReadBlock()); + return Image.FromStream(ms); + } + + private void WriteImage(Image i) + { + fs.WriteBlock(ImageJPEG(i)); + } + + private void Commit() + { + if (readOnly) + throw new Exception("Tebeo de solo lectura, no se puede escribir"); + fs.Position = metapos - sizeof(int); + fs.WriteInt32(0); // End of directory mark + fs.Serialize(this); + if (tboStart > 0) + { + fs.WriteInt32((int)tboStart); + fs.Write(Encoding.ASCII.GetBytes(FILEHEADER)); + } + changed = false; + } + + #endregion + + #region Image Processing + private static ImageCodecInfo GetEncoder(ImageFormat format) { ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders(); @@ -64,107 +170,15 @@ return null; } - public void Dispose() - { - fs.Close(); - } - - private void ReadDirectory() - { - List poses = new List(); - long pos = fs.Position; - int lng; - while ((lng = fs.ReadInt32()) > 0) - { - poses.Add(pos); - fs.Position = pos = fs.Position + lng; - } - pagepos = poses.ToArray(); - } - - private void ReadMetadata() - { - metapos = fs.Position; - if (fs.Position >= fs.Length) - return; - string metaJSON = fs.ReadString(); - } - - #region I/O - - private void ReadHeader() - { - empty = false; - fs.Position = 0; - if (Encoding.ASCII.GetString(fs.Read(FILEHEADER.Length)) != FILEHEADER) - throw new Exception("El arhivo no es un tebeo"); - if (fs.ReadByte() != EOF) - throw new Exception("Error al leer el tebeo"); - if (fs.ReadByte() != FILEVERSION) - throw new Exception("No se puede leer esta versión del tebeo"); - } - - private void WriteHeader() - { - empty = true; - fs.Position = 0; - fs.Write(Encoding.ASCII.GetBytes(FILEHEADER)); - fs.WriteByte(EOF); - fs.WriteByte(FILEVERSION); - } - - private Image ReadImage() - { - MemoryStream ms = new MemoryStream(fs.ReadBlock()); - return Image.FromStream(ms); - } - - private void WriteImage(Image i) - { - fs.WriteBlock(ImageJPEG(i)); - } - - public void AppendImage(string filename) - { - byte[] image; - FileInfo fi = new FileInfo(filename); - Image i; - try - { - i = Image.FromFile(filename); - } - catch - { - return; - } - using (i) - { - image = GetImageBytes(i); - if (image.Length > fi.Length) - image = File.ReadAllBytes(filename); - } - fs.WriteBlock(image); - } - - /// - /// Closes the TBO writing procedure - /// - public void Commit() - { - - } - - #endregion - - private byte[] GetImageBytes(Image i) + private byte[] GetImageBytes(Image i, int grayscale, bool keepColors) { byte[] img = ImageJPEG(i); - if (Grayscale == 0) + if (grayscale == 0) return img; - if (KeepColors && !ImageWorks.IsGray(i)) + if (keepColors && !ImageWorks.IsGray(i)) return img; byte[] gimg; - switch (Grayscale) + switch (grayscale) { case 256: gimg = ImagePNG8bpp(i); @@ -249,6 +263,87 @@ return ImagePNG(bmp); } + #endregion + + /// + /// Reset the file starting like new + /// + public void Reset() + { + changed = true; + fs.Position = tboStart; + fs.SetLength(tboStart); + WriteHeader(); + ReadDirectory(); + } + + /// + /// Reset the TBO with a thumbnail image + /// + /// + public void Reset(Image i) + { + tboStart = 0; + if (i != null) + { + using (Bitmap bmp = new Bitmap(256, 256)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.SmoothingMode = SmoothingMode.AntiAlias; + g.InterpolationMode = InterpolationMode.High; + int w = (i.Width * 256) / i.Height; + g.DrawImage(i, (256 - w) / 2, 0, w, 256); + g.DrawImage(Resources.tbo_logo, 128, 170, 128, 88); + fs.Position = 0; + bmp.Save(fs, ImageFormat.Png); + tboStart = fs.Position; + } + } + Reset(); + } + + /// + /// Adds an image to the TBO + /// + /// + public void AppendImage(string filename, int grayscale = 0, bool keepColors = true) + { + if (readOnly) + throw new Exception("Tebeo de solo lectura, no se puede escribir"); + Image i; + try + { + i = Image.FromFile(filename); + } + catch + { + return; + } + changed = true; + fs.Position = metapos - sizeof(int); + byte[] image; + FileInfo fi = new FileInfo(filename); + using (i) + { + image = GetImageBytes(i, grayscale, keepColors); + if (image.Length > fi.Length) + image = File.ReadAllBytes(filename); + } + pagepos.Add(fs.Position); + fs.WriteBlock(image); + metapos = fs.Position + sizeof(int); + } + + /// + /// Save the metadata of the file + /// + public void SaveMetadata() + { + Commit(); + } + + #region Metadata Properteis + [xIO.Serializable("Titl")] public string Title { get; set; } @@ -270,9 +365,9 @@ [xIO.Serializable("Com")] public string Comments { get; set; } - public int Grayscale { get; set; } = 16; - public bool KeepColors { get; set; } = true; + #endregion public long Size => fs.Length; + public int PageCount => pagepos.Count; } } diff --git a/TBO/UI/Windows/Studio.Designer.cs b/TBO/UI/Windows/Studio.Designer.cs index fc247f0..ae20c52 100644 --- a/TBO/UI/Windows/Studio.Designer.cs +++ b/TBO/UI/Windows/Studio.Designer.cs @@ -58,6 +58,7 @@ this.cbDepth = new System.Windows.Forms.ComboBox(); this.label12 = new System.Windows.Forms.Label(); this.cbColor = new System.Windows.Forms.CheckBox(); + this.cbThumb = new System.Windows.Forms.CheckBox(); ((System.ComponentModel.ISupportInitialize)(this.nudYear)).BeginInit(); this.SuspendLayout(); // @@ -147,7 +148,7 @@ // this.pbGo.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.pbGo.Location = new System.Drawing.Point(12, 410); + this.pbGo.Location = new System.Drawing.Point(12, 431); this.pbGo.Name = "pbGo"; this.pbGo.Size = new System.Drawing.Size(292, 23); this.pbGo.TabIndex = 8; @@ -155,7 +156,7 @@ // btGo // this.btGo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.btGo.Location = new System.Drawing.Point(310, 410); + this.btGo.Location = new System.Drawing.Point(310, 431); this.btGo.Name = "btGo"; this.btGo.Size = new System.Drawing.Size(75, 23); this.btGo.TabIndex = 14; @@ -167,7 +168,7 @@ // this.lbInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.lbInfo.AutoSize = true; - this.lbInfo.Location = new System.Drawing.Point(12, 394); + this.lbInfo.Location = new System.Drawing.Point(12, 415); this.lbInfo.Name = "lbInfo"; this.lbInfo.Size = new System.Drawing.Size(16, 13); this.lbInfo.TabIndex = 10; @@ -318,6 +319,7 @@ this.cbGrayscale.TabIndex = 11; this.cbGrayscale.Text = "Usar"; this.cbGrayscale.UseVisualStyleBackColor = true; + this.cbGrayscale.CheckedChanged += new System.EventHandler(this.cbGrayscale_CheckedChanged); // // cbDepth // @@ -357,12 +359,25 @@ this.cbColor.Text = "Excepto las de color."; this.cbColor.UseVisualStyleBackColor = true; // + // cbThumb + // + this.cbThumb.AutoSize = true; + this.cbThumb.Checked = true; + this.cbThumb.CheckState = System.Windows.Forms.CheckState.Checked; + this.cbThumb.Location = new System.Drawing.Point(15, 389); + this.cbThumb.Name = "cbThumb"; + this.cbThumb.Size = new System.Drawing.Size(225, 17); + this.cbThumb.TabIndex = 28; + this.cbThumb.Text = "Generar un thumbnail de la primera página"; + this.cbThumb.UseVisualStyleBackColor = true; + // // Studio // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; - this.ClientSize = new System.Drawing.Size(397, 445); + this.ClientSize = new System.Drawing.Size(397, 466); + this.Controls.Add(this.cbThumb); this.Controls.Add(this.cbColor); this.Controls.Add(this.label12); this.Controls.Add(this.cbDepth); @@ -437,5 +452,6 @@ private System.Windows.Forms.ComboBox cbDepth; private System.Windows.Forms.Label label12; private System.Windows.Forms.CheckBox cbColor; + private System.Windows.Forms.CheckBox cbThumb; } } \ No newline at end of file diff --git a/TBO/UI/Windows/Studio.cs b/TBO/UI/Windows/Studio.cs index 29378d3..5eef6cf 100644 --- a/TBO/UI/Windows/Studio.cs +++ b/TBO/UI/Windows/Studio.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.IO; using System.Windows.Forms; @@ -55,7 +56,7 @@ if (fbd.ShowDialog() == DialogResult.OK) { tbInputDir.Text = fbd.SelectedPath; - tbTBO.Text = Path.Combine(Path.GetDirectoryName(fbd.SelectedPath), Path.GetFileNameWithoutExtension(fbd.SelectedPath)) + ".tbo"; + tbTBO.Text = Path.Combine(Path.GetDirectoryName(fbd.SelectedPath), Path.GetFileNameWithoutExtension(fbd.SelectedPath)) + ".tebeo.png"; } } @@ -67,8 +68,8 @@ fd.FileName = tbTBO.Text; } catch { } - fd.Filter = "Tebeo|*.tbo"; - fd.DefaultExt = "tbo"; + fd.Filter = "Tebeo|*.tebeo.png"; + fd.DefaultExt = "tebeo.png"; if (fd.ShowDialog() == DialogResult.OK) { tbTBO.Text = fd.FileName; @@ -94,27 +95,39 @@ try { btGo.Enabled = false; - TBOFile tbo = new TBOFile(tbTBO.Text); - tbo.Title = tbTitle.Text; - tbo.Variant = tbVar.Text; - tbo.Volume = tbVolume.Text; - tbo.Author = tbAuthor.Text; - tbo.Publisher = tbPublisher.Text; - tbo.Year = (int)nudYear.Value; - tbo.Comments = tbCom.Text; - if (cbGrayscale.Checked) - tbo.Grayscale = new int[] { 256, 16, 8, 6, 4, 2 }[cbDepth.SelectedIndex]; - else - tbo.Grayscale = 0; - tbo.KeepColors = cbColor.Checked; - for (int i = 0; i < files.Count; i++) + int grayscale = new int[] { 256, 16, 8, 6, 4, 2 }[cbDepth.SelectedIndex]; + bool keepcolors = cbColor.Checked; + using (TBOFile tbo = new TBOFile(tbTBO.Text)) { - pbGo.Value = i; - tbo.AppendImage(files[i]); - lbInfo.Text = FBytes(tbo.Size); - Application.DoEvents(); + if (cbThumb.Checked) + { + Image i = null; + foreach (string f in files) + try + { + i = Image.FromFile(f); + break; + } + catch { }; + using (i) { tbo.Reset(i); } + } + else + tbo.Reset(); + tbo.Title = tbTitle.Text; + tbo.Variant = tbVar.Text; + tbo.Volume = tbVolume.Text; + tbo.Author = tbAuthor.Text; + tbo.Publisher = tbPublisher.Text; + tbo.Year = (int)nudYear.Value; + tbo.Comments = tbCom.Text; + for (int i = 0; i < files.Count; i++) + { + pbGo.Value = i; + tbo.AppendImage(files[i], grayscale, keepcolors); + lbInfo.Text = tbo.PageCount + " páginas (" + FBytes(tbo.Size) + ")"; + Application.DoEvents(); + } } - tbo.W } //catch (Exception ex) //{ @@ -123,13 +136,20 @@ finally { btGo.Enabled = true; + pbGo.Value = pbGo.Maximum; } - + MessageBox.Show("Tebeo creado correctamente", "Tebeo", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void Studio_Load(object sender, EventArgs e) { cbDepth.SelectedIndex = 1; } + + private void cbGrayscale_CheckedChanged(object sender, EventArgs e) + { + cbDepth.Enabled = cbGrayscale.Checked; + cbColor.Enabled = cbGrayscale.Checked; + } } }