diff --git a/.gitignore b/.gitignore index 8fac563..f08f48f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .vs/* -bin/* -obj/* +src/bin/* +src/obj/* publish/* -*.user \ No newline at end of file +src/*.user \ No newline at end of file diff --git a/LICENSE b/LICENSE index c59d59b..06c2cb9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2023, YangFan, theyangfan@163.com +Copyright (c) 2024, YangFan, theyangfan@163.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/PictureViewer.xaml b/PictureViewer.xaml deleted file mode 100644 index 47084a5..0000000 --- a/PictureViewer.xaml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/PictureViewer.xaml.cs b/PictureViewer.xaml.cs deleted file mode 100644 index 2e4f718..0000000 --- a/PictureViewer.xaml.cs +++ /dev/null @@ -1,274 +0,0 @@ -using System; -using System.IO; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Animation; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; -using F = System.Windows.Forms; -using C = System.Windows.Controls; -using System.Drawing; -using System.Drawing.Imaging; -using Word = Microsoft.Office.Interop.Word; - -namespace WordPictureViewer -{ - /// - /// UserControl1.xaml 的交互逻辑 - /// - public partial class PictureViewer : Window - { - #region Private Members - private const int MINIMUM_SCALE = 50; - private int _scale = 100; - private int _scaleStep = 10; - private bool _zoomIn = true; - #endregion - - #region Constructor - public PictureViewer() - { - InitializeComponent(); - ResizeMode = ResizeMode.NoResize; - WindowStartupLocation = WindowStartupLocation.CenterOwner; - WindowState = WindowState.Maximized; - WindowStyle = WindowStyle.None; - AllowsTransparency = true; - - // Mouse wheel event - MouseWheel += PictureViewer_MouseWheel; - } - #endregion - - #region Public Method - /// - /// Show inline shape. - /// - /// - public void ShowInlineShapeSelection(Word.Selection sel) - { - try - { - var shape = sel.InlineShapes[1]; - // get the bytes of the shape - var bits = (byte[])sel.EnhMetaFileBits; - using (MemoryStream stream = new MemoryStream(bits)) - { - Bitmap bitmap = new Bitmap(stream); - // get the available width - double initW = bitmap.Height * (shape.Width / shape.Height); - double initH = bitmap.Height; - // crop the available area - Bitmap cropBmp = Crop(bitmap, 0, 0, (int)initW, (int)initH); - double ratio = (double)initW / initH; - double screenW = SystemParameters.PrimaryScreenWidth; - double screenH = SystemParameters.PrimaryScreenHeight; - if (initW > screenW) - { - initW = screenW; - initH = initW / ratio; - } - if (initH > screenH) - { - initH = screenH; - initW = initH * ratio; - } - initW *= 0.8; - initH *= 0.8; - UIImage.Width = initW; - UIImage.Height = initH; - - // set image source - using (MemoryStream ms = new MemoryStream()) - { - cropBmp.Save(ms, ImageFormat.Png); - byte[] bytes = ms.GetBuffer(); - BitmapImage bi = new BitmapImage(); - bi.BeginInit(); - bi.StreamSource = new MemoryStream(bytes); - bi.EndInit(); - UIImage.Source = bi; - } - // loading animation - double centerX = initW / 2; - double centerY = initH / 2; - DoubleAnimation aniScale = new DoubleAnimation() { Duration = TimeSpan.FromMilliseconds(250) }; - DoubleAnimation aniX = new DoubleAnimation(centerX, centerX, TimeSpan.FromMilliseconds(0)); - DoubleAnimation aniY = new DoubleAnimation(centerY, centerY, TimeSpan.FromMilliseconds(0)); - aniScale.From = 0; - aniScale.To = 1; - UIScale.BeginAnimation(ScaleTransform.ScaleXProperty, aniScale, HandoffBehavior.Compose); - UIScale.BeginAnimation(ScaleTransform.ScaleYProperty, aniScale, HandoffBehavior.Compose); - UIScale.BeginAnimation(ScaleTransform.CenterXProperty, aniX); - UIScale.BeginAnimation(ScaleTransform.CenterYProperty, aniY); - } - } - catch (Exception e) - { - MessageBox.Show($"{e.Message}\n{e.StackTrace}"); - } - } - - /// - /// Show floating shape. - /// - /// - public void ShowFloatingShapeSelection(Word.Selection sel) - { - try - { - // get the bytes of the shape - var bits = (byte[])sel.EnhMetaFileBits; - using (MemoryStream stream = new MemoryStream(bits)) - { - Bitmap bitmap = new Bitmap(stream); - double initW = bitmap.Width; - double initH = bitmap.Height; - double ratio = (double)initW / initH; - double screenW = SystemParameters.PrimaryScreenWidth; - double screenH = SystemParameters.PrimaryScreenHeight; - if (initW > screenW) - { - initW = screenW; - initH = initW / ratio; - } - if (initH > screenH) - { - initH = screenH; - initW = initH * ratio; - } - initW *= 0.8; - initH *= 0.8; - UIImage.Width = initW; - UIImage.Height = initH; - - // Set image source - using (MemoryStream ms = new MemoryStream()) - { - bitmap.Save(ms, ImageFormat.Png); - byte[] bytes = ms.GetBuffer(); - BitmapImage bi = new BitmapImage(); - bi.BeginInit(); - bi.StreamSource = new MemoryStream(bytes); - bi.EndInit(); - UIImage.Source = bi; - } - // loading animation - double centerX = initW / 2; - double centerY = initH / 2; - DoubleAnimation aniScale = new DoubleAnimation() { Duration = TimeSpan.FromMilliseconds(250) }; - DoubleAnimation aniX = new DoubleAnimation(centerX, centerX, TimeSpan.FromMilliseconds(0)); - DoubleAnimation aniY = new DoubleAnimation(centerY, centerY, TimeSpan.FromMilliseconds(0)); - aniScale.From = 0; - aniScale.To = 1; - UIScale.BeginAnimation(ScaleTransform.ScaleXProperty, aniScale, HandoffBehavior.Compose); - UIScale.BeginAnimation(ScaleTransform.ScaleYProperty, aniScale, HandoffBehavior.Compose); - UIScale.BeginAnimation(ScaleTransform.CenterXProperty, aniX); - UIScale.BeginAnimation(ScaleTransform.CenterYProperty, aniY); - } - } - catch (Exception e) - { - MessageBox.Show($"{e.Message}\n{e.StackTrace}"); - } - } - #endregion - - #region Private Methods - - private void PictureViewer_MouseWheel(object sender, MouseWheelEventArgs e) - { - if(e.Delta > 0) // zoom in - { - if(_zoomIn && _scale > 100) _scaleStep++; - _scale += _scaleStep; - _zoomIn = true; - } - else // zoom out - { - if (!_zoomIn && _scale > 100) _scaleStep--; - if (_scale - _scaleStep < MINIMUM_SCALE) return; - _scale -= _scaleStep; - _zoomIn = false; - } - // smooth scale animation - DoubleAnimation aniScale = new DoubleAnimation() { Duration = TimeSpan.FromMilliseconds(250) }; - aniScale.To = (double)_scale / 100; - System.Windows.Point mouse = Mouse.GetPosition(UIImage); - double centerX = mouse.X; - double centerY = mouse.Y; - - DoubleAnimation aniX = new DoubleAnimation(centerX, centerX, TimeSpan.FromMilliseconds(0)); - DoubleAnimation aniY = new DoubleAnimation(centerY, centerY, TimeSpan.FromMilliseconds(0)); - - UIScale.BeginAnimation(ScaleTransform.ScaleXProperty, aniScale, HandoffBehavior.Compose); - UIScale.BeginAnimation(ScaleTransform.ScaleYProperty, aniScale, HandoffBehavior.Compose); - UIScale.BeginAnimation(ScaleTransform.CenterXProperty, aniX); - UIScale.BeginAnimation(ScaleTransform.CenterYProperty, aniY); - UIScaleRatio.Content = $"{_scale} %"; - } - - private System.Drawing.Rectangle GetBitmapRect(Bitmap srcBmp, Word.Shape shape) - { - double height = srcBmp.Height * shape.Height / (shape.Height + Math.Abs(shape.Top)); - double initW = srcBmp.Height * (shape.Width / shape.Height); - double initH = srcBmp.Height; - double x = 0; - if (shape.Left > 0) - { - x = shape.Left * srcBmp.Height / shape.Height; - } - double y = 0; - if (shape.Top > 0) - { - y = shape.Top * srcBmp.Height / shape.Height; - } - return System.Drawing.Rectangle.Empty; - } - - /// - /// Crop the bitmap to the specified size. - /// - /// The original bitmap. - /// The specified width to crop. - /// The specified height to crop. - /// The new bitmap. - private Bitmap Crop(Bitmap srcBmp, int x, int y, int width, int height) - { - System.Drawing.Rectangle cropRect = new System.Drawing.Rectangle(x, y, width, height); - Bitmap cropBmp = new Bitmap(width, height); - using(Graphics g = Graphics.FromImage(cropBmp)) - { - g.DrawImage(srcBmp, new System.Drawing.Rectangle(0, 0, cropBmp.Width, cropBmp.Height), - cropRect, GraphicsUnit.Pixel); - } - return cropBmp; - } - - private void UICloseBtn_Click(object sender, RoutedEventArgs e) - { - Close(); - } - - private void Window_KeyDown(object sender, KeyEventArgs e) - { - if(e.Key == Key.Escape) - { - Close(); - } - } - #endregion - - - - } -} diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs deleted file mode 100644 index 536f5b2..0000000 --- a/Properties/Resources.Designer.cs +++ /dev/null @@ -1,62 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace WordPictureViewer.Properties { - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WordPictureViewer.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - } -} diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs deleted file mode 100644 index df4f1e3..0000000 --- a/Properties/Settings.Designer.cs +++ /dev/null @@ -1,26 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace WordPictureViewer.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - } -} diff --git a/Properties/Settings.settings b/Properties/Settings.settings deleted file mode 100644 index 3964565..0000000 --- a/Properties/Settings.settings +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/README.md b/README.md index 7b6f768..91fde07 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,17 @@ This project is a Word VSTO add-in program (plug-in) for **double-click to enlarge** browse pictures in Office Word documents. -![Example](https://github.com/theyangfan/WordPictureViewer/blob/main/example.gif) +![Example](images/example_01.png) + +![Example02](images/example_02.png) + + ## 下载 (Downloads) -| Office AddIn | Download | -| ------------ | ---------------------------------------------------------------------------------------------------------- | -| Word | [word_v1.0.3.zip](https://github.com/theyangfan/WordPictureViewer/releases/download/1.0.3/word_v1.0.3.zip) | +| Office AddIn | Download | +| ------------ | --------------------------------------------------------------------------------------------------------------------- | +| Word | [word_v1.1.0.zip (20240524)](https://github.com/theyangfan/WordPictureViewer/releases/download/1.1.0/word_v1.1.0.zip) | ## 安装 (Install) @@ -26,6 +30,11 @@ Open Windows Settings, go to 【Apps】 - 【Apps and Features】, search for Wo ## 更新日志 (Release History) +### v1.1.0 (2024-05-24) + +- 更新浏览界面; +- 新增图片保存和使用本机默认照片程序打开功能。 + ### v1.0.3 (2023-09-04) - 最小缩放率调整为 50%。 diff --git a/Ribbon.cs b/Ribbon.cs deleted file mode 100644 index 1ce704a..0000000 --- a/Ribbon.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.Office.Tools.Ribbon; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace WordPictureViewer -{ - public partial class Ribbon - { - public bool IsEnable - { - get => UIEnable.Checked; - set => UIEnable.Checked = value; - } - - private void Ribbon_Load(object sender, RibbonUIEventArgs e) - { - - } - } -} diff --git a/WordPictureViewer.sln b/WordPictureViewer.sln index 477c981..c5f35d9 100644 --- a/WordPictureViewer.sln +++ b/WordPictureViewer.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.2.32630.192 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WordPictureViewer", "WordPictureViewer.csproj", "{371ABB58-3DBF-4945-A624-78EFF7198D0C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WordPictureViewer", "src/WordPictureViewer.csproj", "{371ABB58-3DBF-4945-A624-78EFF7198D0C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/WordPictureViewer_TemporaryKey.pfx b/WordPictureViewer_TemporaryKey.pfx deleted file mode 100644 index 2979099..0000000 Binary files a/WordPictureViewer_TemporaryKey.pfx and /dev/null differ diff --git a/close.png b/close.png deleted file mode 100644 index 24e3388..0000000 Binary files a/close.png and /dev/null differ diff --git a/close_red.png b/close_red.png deleted file mode 100644 index fa98448..0000000 Binary files a/close_red.png and /dev/null differ diff --git a/example.gif b/example.gif deleted file mode 100644 index a65e26e..0000000 Binary files a/example.gif and /dev/null differ diff --git a/images/example_01.png b/images/example_01.png new file mode 100644 index 0000000..6394e9c Binary files /dev/null and b/images/example_01.png differ diff --git a/images/example_02.png b/images/example_02.png new file mode 100644 index 0000000..010ecbc Binary files /dev/null and b/images/example_02.png differ diff --git a/src/PictureViewer.xaml b/src/PictureViewer.xaml new file mode 100644 index 0000000..b6df069 --- /dev/null +++ b/src/PictureViewer.xaml @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/PictureViewer.xaml.cs b/src/PictureViewer.xaml.cs new file mode 100644 index 0000000..e81ada9 --- /dev/null +++ b/src/PictureViewer.xaml.cs @@ -0,0 +1,439 @@ +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using F = System.Windows.Forms; +using C = System.Windows.Controls; +using System.Drawing; +using System.Drawing.Imaging; +using Word = Microsoft.Office.Interop.Word; +using System.Threading; + +namespace WordPictureViewer +{ + /// + /// UserControl1.xaml 的交互逻辑 + /// + public partial class PictureViewer : Window + { + #region Private Members + private const int MINIMUM_SCALE = 50; // 50% + private Bitmap _bitmap = null; + private double _oriWidth; + private double _oriHeight; + private int _scale = 100; // 100% + private int _scaleStep = 10; + private bool _isZoomIn = true; + + private System.Windows.Point _pressedPoint; + private bool _isPressed = false; + private double _lastXOffset = 0; + private double _lastYOffset = 0; + #endregion + + #region Constructor + public PictureViewer() + { + InitializeComponent(); + + ResizeMode = ResizeMode.NoResize; + WindowStartupLocation = WindowStartupLocation.CenterOwner; + WindowState = WindowState.Maximized; + WindowStyle = WindowStyle.None; + AllowsTransparency = true; + + InitUI(); + + // Mouse events + MouseWheel += PictureViewer_MouseWheel; + + uiContent.MouseDown += PictureViewer_MouseDown; + + uiContent.MouseUp += PictureViewer_MouseUp; + + uiContent.MouseMove += PictureViewer_MouseMove; + + _pressedPoint = new System.Windows.Point(0, 0); + } + + + #endregion + + #region Public Method + /// + /// Show inline shape. + /// + /// + public void ShowInlineShapeSelection(Word.Selection sel) + { + try + { + var shape = sel.InlineShapes[1]; + // get the bytes of the shape + var bits = (byte[])sel.EnhMetaFileBits; + using (MemoryStream stream = new MemoryStream(bits)) + { + Bitmap srcBmp = new Bitmap(stream); + // get the available width + _oriWidth = srcBmp.Height * (shape.Width / shape.Height); + _oriHeight = srcBmp.Height; + // crop the available area + _bitmap = Crop(srcBmp, 0, 0, (int)_oriWidth, (int)_oriHeight); + lblPicSize.Content = $"{_bitmap.Width} x {_bitmap.Height}"; + double ratio = (double)_oriWidth / _oriHeight; + double screenW = SystemParameters.PrimaryScreenWidth; + double screenH = SystemParameters.PrimaryScreenHeight; + if (_oriWidth > screenW) + { + _oriWidth = screenW; + _oriHeight = _oriWidth / ratio; + } + if (_oriHeight > screenH) + { + _oriHeight = screenH; + _oriWidth = _oriHeight * ratio; + } + _oriWidth *= 0.8; + _oriHeight *= 0.8; + uiImage.Width = _oriWidth; + uiImage.Height = _oriHeight; + + // set image source + using (MemoryStream ms = new MemoryStream()) + { + _bitmap.Save(ms, ImageFormat.Png); + var bytes = ms.GetBuffer(); + BitmapImage bi = new BitmapImage(); + bi.BeginInit(); + bi.StreamSource = new MemoryStream(bytes); + bi.EndInit(); + uiImage.Source = bi; + } + // loading animation + Scale(0, 1, _oriWidth / 2, _oriHeight / 2); + } + } + catch (Exception e) + { + MessageBox.Show($"{e.Message}\n{e.StackTrace}"); + } + } + + /// + /// Show floating shape. + /// + /// + public void ShowFloatingShapeSelection(Word.Selection sel) + { + try + { + // get the bytes of the shape + var bits = (byte[])sel.EnhMetaFileBits; + using (MemoryStream stream = new MemoryStream(bits)) + { + _bitmap = new Bitmap(stream); + lblPicSize.Content = $"{_bitmap.Width} x {_bitmap.Height}"; + _oriWidth = _bitmap.Width; + _oriHeight = _bitmap.Height; + double ratio = (double)_oriWidth / _oriHeight; + double screenW = SystemParameters.PrimaryScreenWidth; + double screenH = SystemParameters.PrimaryScreenHeight; + if (_oriWidth > screenW) + { + _oriWidth = screenW; + _oriHeight = _oriWidth / ratio; + } + if (_oriHeight > screenH) + { + _oriHeight = screenH; + _oriWidth = _oriHeight * ratio; + } + _oriWidth *= 0.8; + _oriHeight *= 0.8; + uiImage.Width = _oriWidth; + uiImage.Height = _oriHeight; + + // Set image source + using (MemoryStream ms = new MemoryStream()) + { + _bitmap.Save(ms, ImageFormat.Png); + var bytes = ms.GetBuffer(); + BitmapImage bi = new BitmapImage(); + bi.BeginInit(); + bi.StreamSource = new MemoryStream(bytes); + bi.EndInit(); + uiImage.Source = bi; + } + // loading animation + Scale(0, 1, _oriWidth / 2, _oriHeight / 2); + } + } + catch (Exception e) + { + MessageBox.Show($"{e.Message}\n{e.StackTrace}"); + } + } + #endregion + + #region Private Methods + private void InitUI() + { + // set button tooltip + uiLogo.ToolTip = ResourceHelper.Strings.GetString("AppName"); + uiBtnSave.ToolTip = ResourceHelper.Strings.GetString("SaveAs"); + uiBtnOpenWith.ToolTip = ResourceHelper.Strings.GetString("OpenWith"); + uiBtnZoomIn.ToolTip = ResourceHelper.Strings.GetString("ZoomIn"); + uiBtnZoomOut.ToolTip = ResourceHelper.Strings.GetString("ZoomOut"); + uiBtnCentered.ToolTip = ResourceHelper.Strings.GetString("AlignCenter"); + } + + private void ZoomIn() + { + double scale = (double)_scale / 100; + // scale step increase when zoom in + if (_isZoomIn && _scale > 100) _scaleStep++; + + _scale += _scaleStep; + _isZoomIn = true; + double newScale = (double)_scale / 100; + + Scale(scale, newScale, _oriWidth / 2, _oriHeight / 2); + + if (!uiBtnZoomOut.IsEnabled) + { + uiBtnZoomOut.IsEnabled = true; + } + } + + private void ZoomOut() + { + double scale = (double)_scale / 100; + // scale step decrease when zoom out + if (!_isZoomIn && _scale > 100) _scaleStep--; + + if (_scale - _scaleStep < MINIMUM_SCALE) return; + _scale -= _scaleStep; + _isZoomIn = false; + double newScale = (double)_scale / 100; + + Scale(scale, newScale, _oriWidth / 2, _oriHeight / 2); + + if(_scale <= MINIMUM_SCALE) + { + uiBtnZoomOut.IsEnabled = false; + } + } + + private void Scale(double from, double to, double centerX, double centerY) + { + uiScale.CenterX = centerX; + uiScale.CenterY = centerY; + // smooth scale animation + DoubleAnimation aniScale = new DoubleAnimation() { Duration = TimeSpan.FromMilliseconds(250) }; + aniScale.From = from; + aniScale.To = to; + uiScale.BeginAnimation(ScaleTransform.ScaleXProperty, aniScale, HandoffBehavior.Compose); + uiScale.BeginAnimation(ScaleTransform.ScaleYProperty, aniScale, HandoffBehavior.Compose); + uiScaleRatio.Content = $"{_scale} %"; + } + + private System.Drawing.Rectangle GetBitmapRect(Bitmap srcBmp, Word.Shape shape) + { + double height = srcBmp.Height * shape.Height / (shape.Height + Math.Abs(shape.Top)); + double initW = srcBmp.Height * (shape.Width / shape.Height); + double initH = srcBmp.Height; + double x = 0; + if (shape.Left > 0) + { + x = shape.Left * srcBmp.Height / shape.Height; + } + double y = 0; + if (shape.Top > 0) + { + y = shape.Top * srcBmp.Height / shape.Height; + } + return System.Drawing.Rectangle.Empty; + } + + /// + /// Crop the bitmap to the specified size. + /// + /// The original bitmap. + /// The specified width to crop. + /// The specified height to crop. + /// The new bitmap. + private Bitmap Crop(Bitmap srcBmp, int x, int y, int width, int height) + { + System.Drawing.Rectangle cropRect = new System.Drawing.Rectangle(x, y, width, height); + Bitmap cropBmp = new Bitmap(width, height); + using (Graphics g = Graphics.FromImage(cropBmp)) + { + g.DrawImage(srcBmp, new System.Drawing.Rectangle(0, 0, cropBmp.Width, cropBmp.Height), + cropRect, GraphicsUnit.Pixel); + } + return cropBmp; + } + + private ImageFormat GetImageFormat(string extension) + { + extension = extension.ToLower(); + switch (extension) + { + case ".bmp": + return ImageFormat.Bmp; + case ".jpg": + case ".jpeg": + return ImageFormat.Jpeg; + case ".png": + return ImageFormat.Png; + case ".gif": + return ImageFormat.Gif; + case ".emf": + return ImageFormat.Emf; + case ".wmf": + return ImageFormat.Wmf; + default: + return ImageFormat.Bmp; + } + } + #endregion + + #region Event Handler + private void PictureViewer_MouseWheel(object sender, MouseWheelEventArgs e) + { + if (e.Delta > 0) + { + ZoomIn(); + } + else + { + ZoomOut(); + } + } + + private void PictureViewer_MouseUp(object sender, MouseButtonEventArgs e) + { + _isPressed = false; + _lastXOffset = uiTranslate.X; + _lastYOffset = uiTranslate.Y; + uiContent.Cursor = Cursors.Arrow; + } + + private void PictureViewer_MouseDown(object sender, MouseButtonEventArgs e) + { + if(e.ChangedButton == MouseButton.Left) + { + _isPressed = true; + _pressedPoint = Mouse.GetPosition(uiContent); + uiContent.Cursor = Cursors.SizeAll; + } + } + + private void PictureViewer_MouseMove(object sender, MouseEventArgs e) + { + if (!_isPressed) return; + var mouse = Mouse.GetPosition(uiContent); + var offset = mouse - _pressedPoint; + + offset.X /= ((double)_scale / 100); + offset.Y /= ((double)_scale / 100); + + uiTranslate.X = _lastXOffset + offset.X; + uiTranslate.Y = _lastYOffset + offset.Y; + } + + private void btnSave_Click(object sender, RoutedEventArgs e) + { + try + { + if (_bitmap == null) + { + MessageBox.Show("Invalid Picture!"); + return; + } + Microsoft.Win32.SaveFileDialog save = new Microsoft.Win32.SaveFileDialog(); + save.Filter = "Jpeg Files (*.jpg, *.jpeg)|*.jpg;*.jpeg | Png files (*.png) |*.png | Bmp Files (*.bmp)|*.bmp " + + "| Gif Files (*.gif)|*.gif | Emf Files (*.emf)|*.emf | All Files (*.*)|*.*"; + if (save.ShowDialog() != true) return; + string ext = new FileInfo(save.FileName).Extension; + ImageFormat format = GetImageFormat(ext); + + _bitmap.Save(save.FileName, format); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + } + + private void btnOpenWith_Click(object sender, RoutedEventArgs e) + { + try + { + if (_bitmap == null) + { + MessageBox.Show("Invalid Picture!"); + return; + } + string tempFile = System.IO.Path.Combine(System.IO.Path.GetTempPath(), $"WordPictureViewer-{Guid.NewGuid()}.jpg"); + _bitmap.Save(tempFile, ImageFormat.Jpeg); + System.Diagnostics.Process.Start(tempFile); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + + } + + private void btnClose_Click(object sender, RoutedEventArgs e) + { + if(_bitmap != null) _bitmap.Dispose(); + Close(); + } + + private void btnZoomOut_Click(object sender, RoutedEventArgs e) + { + ZoomOut(); + } + + private void btnZoomIn_Click(object sender, RoutedEventArgs e) + { + ZoomIn(); + } + + private void btnCentered_Click(object sender, RoutedEventArgs e) + { + // reset translate offset + uiTranslate.X = 0; + uiTranslate.Y = 0; + _lastXOffset = 0; + _lastYOffset = 0; + } + + private void Window_KeyDown(object sender, KeyEventArgs e) + { + if(e.Key == Key.Escape) + { + if (_bitmap != null) _bitmap.Dispose(); + Close(); + } + } + + + + #endregion + } +} diff --git a/Properties/AssemblyInfo.cs b/src/Properties/AssemblyInfo.cs similarity index 86% rename from Properties/AssemblyInfo.cs rename to src/Properties/AssemblyInfo.cs index 67997ee..6779c5c 100644 --- a/Properties/AssemblyInfo.cs +++ b/src/Properties/AssemblyInfo.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System.Resources; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; @@ -11,7 +12,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("WordPictureViewer")] -[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyCopyright("Copyright © 2024")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -33,6 +34,5 @@ //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, // 方法是按如下所示使用“*”: : // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] - +[assembly: AssemblyVersion("1.1.0.0")] +[assembly: AssemblyFileVersion("1.1.0.0")] diff --git a/src/Resources/Images/center.png b/src/Resources/Images/center.png new file mode 100644 index 0000000..fdc255e Binary files /dev/null and b/src/Resources/Images/center.png differ diff --git a/src/Resources/Images/close.png b/src/Resources/Images/close.png new file mode 100644 index 0000000..85fc6de Binary files /dev/null and b/src/Resources/Images/close.png differ diff --git a/src/Resources/Images/logo.png b/src/Resources/Images/logo.png new file mode 100644 index 0000000..5421342 Binary files /dev/null and b/src/Resources/Images/logo.png differ diff --git a/src/Resources/Images/save.png b/src/Resources/Images/save.png new file mode 100644 index 0000000..e479bbe Binary files /dev/null and b/src/Resources/Images/save.png differ diff --git a/src/Resources/Images/share.png b/src/Resources/Images/share.png new file mode 100644 index 0000000..b0fe348 Binary files /dev/null and b/src/Resources/Images/share.png differ diff --git a/src/Resources/Images/zoom_in.png b/src/Resources/Images/zoom_in.png new file mode 100644 index 0000000..89caebf Binary files /dev/null and b/src/Resources/Images/zoom_in.png differ diff --git a/src/Resources/Images/zoom_out.png b/src/Resources/Images/zoom_out.png new file mode 100644 index 0000000..40413e7 Binary files /dev/null and b/src/Resources/Images/zoom_out.png differ diff --git a/src/Resources/Images/zoom_out_disabled.png b/src/Resources/Images/zoom_out_disabled.png new file mode 100644 index 0000000..bda766f Binary files /dev/null and b/src/Resources/Images/zoom_out_disabled.png differ diff --git a/src/Resources/Strings.Designer.cs b/src/Resources/Strings.Designer.cs new file mode 100644 index 0000000..7de05d5 --- /dev/null +++ b/src/Resources/Strings.Designer.cs @@ -0,0 +1,135 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace WordPictureViewer.Resources { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Strings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Strings() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WordPictureViewer.Resources.Strings", typeof(Strings).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 Align Center 的本地化字符串。 + /// + internal static string AlignCenter { + get { + return ResourceManager.GetString("AlignCenter", resourceCulture); + } + } + + /// + /// 查找类似 Word Picture Viewer 的本地化字符串。 + /// + internal static string AppName { + get { + return ResourceManager.GetString("AppName", resourceCulture); + } + } + + /// + /// 查找类似 Close 的本地化字符串。 + /// + internal static string Close { + get { + return ResourceManager.GetString("Close", resourceCulture); + } + } + + /// + /// 查找类似 Enable 的本地化字符串。 + /// + internal static string Enable { + get { + return ResourceManager.GetString("Enable", resourceCulture); + } + } + + /// + /// 查找类似 Open With Photos 的本地化字符串。 + /// + internal static string OpenWith { + get { + return ResourceManager.GetString("OpenWith", resourceCulture); + } + } + + /// + /// 查找类似 Save As 的本地化字符串。 + /// + internal static string SaveAs { + get { + return ResourceManager.GetString("SaveAs", resourceCulture); + } + } + + /// + /// 查找类似 Zoom In 的本地化字符串。 + /// + internal static string ZoomIn { + get { + return ResourceManager.GetString("ZoomIn", resourceCulture); + } + } + + /// + /// 查找类似 Zoom Out 的本地化字符串。 + /// + internal static string ZoomOut { + get { + return ResourceManager.GetString("ZoomOut", resourceCulture); + } + } + } +} diff --git a/src/Resources/Strings.resx b/src/Resources/Strings.resx new file mode 100644 index 0000000..1ca8e03 --- /dev/null +++ b/src/Resources/Strings.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Align Center + + + Word Picture Viewer + + + Close + + + Enable + + + Open With Photos + + + Save As + + + Zoom In + + + Zoom Out + + \ No newline at end of file diff --git a/src/Resources/Strings.zh-CN.Designer.cs b/src/Resources/Strings.zh-CN.Designer.cs new file mode 100644 index 0000000..e69de29 diff --git a/Properties/Resources.resx b/src/Resources/Strings.zh-CN.resx similarity index 80% rename from Properties/Resources.resx rename to src/Resources/Strings.zh-CN.resx index af7dbeb..40c6eaa 100644 --- a/Properties/Resources.resx +++ b/src/Resources/Strings.zh-CN.resx @@ -46,7 +46,7 @@ mimetype: application/x-microsoft.net.object.binary.base64 value : The object must be serialized with - : System.Serialization.Formatters.Binary.BinaryFormatter + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.soap.base64 @@ -60,6 +60,7 @@ : and then encoded with base64 encoding. --> + @@ -68,9 +69,10 @@ - + + @@ -85,9 +87,10 @@ - + + @@ -109,9 +112,33 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 居中 + + + 图片浏览器 + + + 关闭 + + + 启用 + + + 在照片中打开 + + + 另存为 + + + 放大 + + + 缩小 + \ No newline at end of file diff --git a/Ribbon.Designer.cs b/src/Ribbon.Designer.cs similarity index 76% rename from Ribbon.Designer.cs rename to src/Ribbon.Designer.cs index fd337f3..c3282f6 100644 --- a/Ribbon.Designer.cs +++ b/src/Ribbon.Designer.cs @@ -11,16 +11,7 @@ public Ribbon() : base(Globals.Factory.GetRibbonFactory()) { InitializeComponent(); - if (System.Globalization.CultureInfo.CurrentCulture.NativeName.StartsWith("中文")) - { - tab1.Label = "图片浏览器"; - UIEnable.Label = "启用"; - } - else - { - tab1.Label = "PictureViewer"; - UIEnable.Label = "Enable"; - } + tab1.Label = ResourceHelper.Strings.GetString("AppName"); } /// @@ -46,7 +37,8 @@ private void InitializeComponent() { this.tab1 = this.Factory.CreateRibbonTab(); this.group1 = this.Factory.CreateRibbonGroup(); - this.UIEnable = this.Factory.CreateRibbonCheckBox(); + this.uiEnable = this.Factory.CreateRibbonCheckBox(); + this.uiVersion = this.Factory.CreateRibbonLabel(); this.tab1.SuspendLayout(); this.group1.SuspendLayout(); this.SuspendLayout(); @@ -55,21 +47,26 @@ private void InitializeComponent() // this.tab1.ControlId.ControlIdType = Microsoft.Office.Tools.Ribbon.RibbonControlIdType.Office; this.tab1.Groups.Add(this.group1); - this.tab1.Label = "图片浏览器"; + this.tab1.Label = "WordPictureViewer"; this.tab1.Name = "tab1"; this.tab1.Tag = ""; // // group1 // - this.group1.Items.Add(this.UIEnable); - this.group1.Label = "选项"; + this.group1.Items.Add(this.uiEnable); + this.group1.Items.Add(this.uiVersion); this.group1.Name = "group1"; // - // UIEnable + // uiEnable + // + this.uiEnable.Checked = true; + this.uiEnable.Label = "启用"; + this.uiEnable.Name = "uiEnable"; + // + // uiVersion // - this.UIEnable.Checked = true; - this.UIEnable.Label = "启用"; - this.UIEnable.Name = "UIEnable"; + this.uiVersion.Label = " "; + this.uiVersion.Name = "uiVersion"; // // Ribbon // @@ -87,8 +84,9 @@ private void InitializeComponent() #endregion internal Microsoft.Office.Tools.Ribbon.RibbonGroup group1; - internal Microsoft.Office.Tools.Ribbon.RibbonCheckBox UIEnable; + internal Microsoft.Office.Tools.Ribbon.RibbonCheckBox uiEnable; internal Microsoft.Office.Tools.Ribbon.RibbonTab tab1; + internal Microsoft.Office.Tools.Ribbon.RibbonLabel uiVersion; } partial class ThisRibbonCollection diff --git a/src/Ribbon.cs b/src/Ribbon.cs new file mode 100644 index 0000000..d9f9987 --- /dev/null +++ b/src/Ribbon.cs @@ -0,0 +1,25 @@ +using Microsoft.Office.Tools.Ribbon; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace WordPictureViewer +{ + public partial class Ribbon + { + public bool IsEnable + { + get => uiEnable.Checked; + set => uiEnable.Checked = value; + } + + private void Ribbon_Load(object sender, RibbonUIEventArgs e) + { + uiEnable.Label = ResourceHelper.Strings.GetString("Enable"); + Version version = Assembly.GetExecutingAssembly().GetName().Version; + uiVersion.Label = $"(V{version.Major}.{version.Minor}.{version.Build})"; + } + } +} diff --git a/Ribbon.resx b/src/Ribbon.resx similarity index 100% rename from Ribbon.resx rename to src/Ribbon.resx diff --git a/ThisAddIn.Designer.cs b/src/ThisAddIn.Designer.cs similarity index 100% rename from ThisAddIn.Designer.cs rename to src/ThisAddIn.Designer.cs diff --git a/ThisAddIn.Designer.xml b/src/ThisAddIn.Designer.xml similarity index 100% rename from ThisAddIn.Designer.xml rename to src/ThisAddIn.Designer.xml diff --git a/ThisAddIn.cs b/src/ThisAddIn.cs similarity index 98% rename from ThisAddIn.cs rename to src/ThisAddIn.cs index bd59c36..eecd133 100644 --- a/ThisAddIn.cs +++ b/src/ThisAddIn.cs @@ -11,6 +11,7 @@ using System.Drawing; using System.Windows.Media; using System.Windows.Media.Imaging; +using System.Globalization; namespace WordPictureViewer { diff --git a/src/Utils/ResourceHelper.cs b/src/Utils/ResourceHelper.cs new file mode 100644 index 0000000..6858807 --- /dev/null +++ b/src/Utils/ResourceHelper.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Resources; +using System.Text; +using System.Threading.Tasks; + +namespace WordPictureViewer +{ + internal class ResourceHelper + { + private static ResourceManager _resourceManager; + + /// + /// The localizable strings resource. + /// + public static ResourceManager Strings + { + get + { + if (_resourceManager == null) + { + _resourceManager = new ResourceManager("WordPictureViewer.Resources.Strings", typeof(ResourceHelper).Assembly); + } + return _resourceManager; + } + } + } +} diff --git a/WordPictureViewer.csproj b/src/WordPictureViewer.csproj similarity index 88% rename from WordPictureViewer.csproj rename to src/WordPictureViewer.csproj index 37578fa..32fbadf 100644 --- a/WordPictureViewer.csproj +++ b/src/WordPictureViewer.csproj @@ -30,10 +30,10 @@ VSTO40 False true - publish\ + ..\publish\ zh-chs - 1.0.3.0 + 1.1.0.0 false true 7 @@ -189,25 +189,27 @@ PictureViewer.xaml - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - + + Strings.zh-CN.resx True - Resources.resx + True + + + ResXFileCodeGenerator + Strings.zh-CN.Designer.cs + + + ResXFileCodeGenerator + Strings.Designer.cs + Ribbon.cs - - SettingsSingleFileGenerator - Settings.Designer.cs - - + True - Settings.settings + True + Strings.resx Component @@ -234,10 +236,26 @@ - + + + + + + + + + + + + + + + + + - + 10.0 @@ -250,7 +268,7 @@ WordPictureViewer_TemporaryKey.pfx - ABF83C4DE1FFCDD83BE6A8D0F028A17982E6970D + BC6C0E0868AC98BB40D982682035B618A0FBD2E9 diff --git a/src/WordPictureViewer_TemporaryKey.pfx b/src/WordPictureViewer_TemporaryKey.pfx new file mode 100644 index 0000000..178fd99 Binary files /dev/null and b/src/WordPictureViewer_TemporaryKey.pfx differ