From 6385aada98da337c2fa474fa82381b25a993b13c Mon Sep 17 00:00:00 2001 From: jlucansky Date: Sat, 25 Jul 2020 19:31:30 +0200 Subject: [PATCH] Copy selection to RTF format --- Source/Swiddler/Channels/SnifferChannel.cs | 28 +- Source/Swiddler/IO/DataTransfer.cs | 8 +- Source/Swiddler/IO/IDataTransferTarget.cs | 82 +++ Source/Swiddler/Properties/AssemblyInfo.cs | 2 +- Source/Swiddler/Rendering/FragmentView.cs | 15 +- .../Swiddler/Rendering/FragmentViewContent.cs | 24 + Source/Swiddler/Rendering/TextFragment.cs | 17 +- Source/Swiddler/Swiddler.csproj | 9 +- .../Swiddler/Utils/RtfWriter/RtfAbstract.cs | 57 ++ Source/Swiddler/Utils/RtfWriter/RtfBasics.cs | 574 ++++++++++++++++++ .../Swiddler/Utils/RtfWriter/RtfBlockList.cs | 115 ++++ .../Swiddler/Utils/RtfWriter/RtfCharFormat.cs | 319 ++++++++++ .../Swiddler/Utils/RtfWriter/RtfDocument.cs | 95 +++ .../Swiddler/Utils/RtfWriter/RtfParagraph.cs | 394 ++++++++++++ Source/Swiddler/Utils/RtfWriter/RtfUtility.cs | 91 +++ Source/Swiddler/Views/MainWindow.xaml | 1 + Source/Swiddler/Views/MainWindow.xaml.cs | 2 +- Source/Swiddler/Views/NewCertificate.xaml | 4 +- Source/Swiddler/Views/NewConnection.xaml.cs | 4 +- .../Swiddler/Views/SslHandshakeDetails.xaml | 12 +- 20 files changed, 1816 insertions(+), 37 deletions(-) create mode 100644 Source/Swiddler/IO/IDataTransferTarget.cs create mode 100644 Source/Swiddler/Utils/RtfWriter/RtfAbstract.cs create mode 100644 Source/Swiddler/Utils/RtfWriter/RtfBasics.cs create mode 100644 Source/Swiddler/Utils/RtfWriter/RtfBlockList.cs create mode 100644 Source/Swiddler/Utils/RtfWriter/RtfCharFormat.cs create mode 100644 Source/Swiddler/Utils/RtfWriter/RtfDocument.cs create mode 100644 Source/Swiddler/Utils/RtfWriter/RtfParagraph.cs create mode 100644 Source/Swiddler/Utils/RtfWriter/RtfUtility.cs diff --git a/Source/Swiddler/Channels/SnifferChannel.cs b/Source/Swiddler/Channels/SnifferChannel.cs index 096644a..617cb3b 100644 --- a/Source/Swiddler/Channels/SnifferChannel.cs +++ b/Source/Swiddler/Channels/SnifferChannel.cs @@ -71,6 +71,15 @@ private class Mediator : Channel public Mediator(Session session) : base(session) { } protected override void OnReceiveNotification(Packet packet) => throw new NotImplementedException(); public void Send(Packet packet) => NotifyObservers(packet); // write to session UI + + public void Close(string reason) + { + HandleError(new Exception(reason)); + } + public void ClosingDueFlag(string flag, IPEndPoint source) + { + Close($"Closing due to {flag} flag sent from {source}"); + } } public SnifferChannel(Session session) : base(session) { } @@ -118,12 +127,15 @@ protected override void StartOverride() void StartAsAdmin() { - Application.Current.Dispatcher.BeginInvoke(new Action(() => + if (!string.IsNullOrEmpty(Session.SettingsFileName)) { - if (MessageBox.Show( - "You don’t have permission to create raw sockets.\n\nDo you want to launch Swiddler as Administrator?", - "Access Denied", MessageBoxButton.YesNo, MessageBoxImage.Exclamation) == MessageBoxResult.Yes) Session.StartAsAdmin(); - })); + Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + if (MessageBox.Show( + "You don’t have permission to create raw sockets.\n\nDo you want to launch Swiddler as Administrator?", + "Access Denied", MessageBoxButton.YesNo, MessageBoxImage.Exclamation) == MessageBoxResult.Yes) Session.StartAsAdmin(); + })); + } } void BeginReceive() @@ -172,10 +184,8 @@ private void ReceiveCallback(IAsyncResult result) mediator.Send(packet); - if (raw.Flags.HasFlag(TCPFlags.RST) || raw.Flags.HasFlag(TCPFlags.FIN)) - { - mediator.Session.Stop(); - } + if (raw.Flags.HasFlag(TCPFlags.RST)) mediator.ClosingDueFlag("RST", raw.Source); + if (raw.Flags.HasFlag(TCPFlags.FIN)) mediator.ClosingDueFlag("FIN", raw.Source); } } } diff --git a/Source/Swiddler/IO/DataTransfer.cs b/Source/Swiddler/IO/DataTransfer.cs index 3482b53..ac420ad 100644 --- a/Source/Swiddler/IO/DataTransfer.cs +++ b/Source/Swiddler/IO/DataTransfer.cs @@ -19,9 +19,11 @@ public class DataTransfer readonly StorageHandle storage; - readonly Stream target; + readonly IDataTransferTarget target; - public DataTransfer(StorageHandle storage, Stream target) + public DataTransfer(StorageHandle storage, Stream target) : this (storage, new StreamTransferTarget(target)) { } + + public DataTransfer(StorageHandle storage, IDataTransferTarget target) { this.storage = storage; this.target = target; @@ -41,7 +43,7 @@ void Copy(BlockReader reader, Func payload) while (reader.CurrentChunk != null && null != (data = payload(reader.CurrentChunk))) { if (data.Length > 0) - target.Write(data, 0, data.Length); + target.Write(reader.CurrentChunk, data); reader.Read(); CancellationToken.ThrowIfCancellationRequested(); diff --git a/Source/Swiddler/IO/IDataTransferTarget.cs b/Source/Swiddler/IO/IDataTransferTarget.cs new file mode 100644 index 0000000..c8538d5 --- /dev/null +++ b/Source/Swiddler/IO/IDataTransferTarget.cs @@ -0,0 +1,82 @@ +using Swiddler.Common; +using Swiddler.DataChunks; +using Swiddler.Utils.RtfWriter; +using System.IO; +using System.Text; + +namespace Swiddler.IO +{ + public interface IDataTransferTarget + { + void Write(IDataChunk chunk, byte[] data); + void Flush(); + } + + public class StreamTransferTarget : IDataTransferTarget + { + readonly Stream _stream; + public StreamTransferTarget(Stream stream) => _stream = stream; + public void Write(IDataChunk chunk, byte[] data) => _stream.Write(data, 0, data.Length); + public void Flush() => _stream.Flush(); + } + + public class RtfTransferTarget : IDataTransferTarget + { + RtfDocument _doc; + ColorDescriptor _inFg, _inBg, _outFg, _outBg; + Encoding _encoding { get; set; } + + public RtfTransferTarget(RtfDocument doc, Encoding encoding) + { + _doc = doc; + _encoding = encoding; + + doc.DefaultCharFormat.FontSize = 10; + + _inFg = doc.createColor(new RtfColor(App.Current.Res.InboundFlowTextBrush.Color)); + _inBg = doc.createColor(new RtfColor(App.Current.Res.InboundFlowBrush.Color)); + _outFg = doc.createColor(new RtfColor(App.Current.Res.OutboundFlowTextBrush.Color)); + _outBg = doc.createColor(new RtfColor(App.Current.Res.OutboundFlowBrush.Color)); + } + + public void Write(IDataChunk chunk, byte[] data) + { + var par = _doc.addParagraph(); + + par.setText(_encoding.GetString(data)); + + if (chunk is Packet packet) + { + if (packet.Flow == TrafficFlow.Inbound) + { + par.DefaultCharFormat.FgColor = _inFg; + par.DefaultCharFormat.BgColor = _inBg; + } + if (packet.Flow == TrafficFlow.Outbound) + { + par.DefaultCharFormat.FgColor = _outFg; + par.DefaultCharFormat.BgColor = _outBg; + } + } + } + + public void Flush() + { + } + } + + public class CompositeTransferTarget : IDataTransferTarget + { + IDataTransferTarget[] _targets; + public CompositeTransferTarget(params IDataTransferTarget[] targets) => _targets = targets; + public void Write(IDataChunk chunk, byte[] data) + { + foreach (var t in _targets) t.Write(chunk, data); + } + public void Flush() + { + foreach (var t in _targets) t.Flush(); + } + } + +} diff --git a/Source/Swiddler/Properties/AssemblyInfo.cs b/Source/Swiddler/Properties/AssemblyInfo.cs index 865ec62..a1639e9 100644 --- a/Source/Swiddler/Properties/AssemblyInfo.cs +++ b/Source/Swiddler/Properties/AssemblyInfo.cs @@ -52,4 +52,4 @@ [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: AssemblyInformationalVersion("1.0.2")] +[assembly: AssemblyInformationalVersion("1.0.3")] diff --git a/Source/Swiddler/Rendering/FragmentView.cs b/Source/Swiddler/Rendering/FragmentView.cs index 5439d27..1a4a65b 100644 --- a/Source/Swiddler/Rendering/FragmentView.cs +++ b/Source/Swiddler/Rendering/FragmentView.cs @@ -1,5 +1,6 @@ using Swiddler.Common; using Swiddler.IO; +using Swiddler.Utils.RtfWriter; using System; using System.Collections.Generic; using System.Diagnostics; @@ -77,7 +78,7 @@ public void CreateContent() Session = CurrentSession, Metrics = new FragmentViewMetrics() { - Encoding = Encoding.Default, + Encoding = Encoding.GetEncoding(437), // IBM 437 (OEM-US) Typeface = new Typeface("Lucida Console"), FontSize = 14, } @@ -325,9 +326,17 @@ public void CopySelection(object sender, ExecutedRoutedEventArgs e) { using (var stream = new MemoryStream()) { - var transfer = new DataTransfer(CurrentSession.Storage, stream); + var rtf = new RtfDocument(); + var target = new CompositeTransferTarget(new StreamTransferTarget(stream), new RtfTransferTarget(rtf, Metrics.Encoding)); + + var transfer = new DataTransfer(CurrentSession.Storage, target); transfer.CopySelection(Content.SelectionStart, Content.SelectionEnd); - Clipboard.SetDataObject(Metrics.Encoding.GetString(stream.ToArray()), true); + + DataObject data = new DataObject(); + data.SetData(DataFormats.Text, Metrics.Encoding.GetString(stream.ToArray())); + data.SetData(DataFormats.Rtf, rtf.render()); + + Clipboard.SetDataObject(data); } } catch (Exception ex) diff --git a/Source/Swiddler/Rendering/FragmentViewContent.cs b/Source/Swiddler/Rendering/FragmentViewContent.cs index c27c6ba..44c3d24 100644 --- a/Source/Swiddler/Rendering/FragmentViewContent.cs +++ b/Source/Swiddler/Rendering/FragmentViewContent.cs @@ -57,6 +57,14 @@ internal void ComputeDpi() FontChanged(); } + static readonly byte[] _sweep = Enumerable.Range(32, 256-32).Select(x => (byte)x).ToArray(); + static readonly char[] _controlChars = new ushort[] { // Code Page 437 to Unicode mapping + 0x0000, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, + 0x25D8, 0x0020, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C, + 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8, + 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC } + .Select(x => (char)x).ToArray(); + private GlyphTypeface _GlyphTypeface; public GlyphTypeface GlyphTypeface { @@ -66,10 +74,26 @@ public GlyphTypeface GlyphTypeface if (!Metrics.Typeface.TryGetGlyphTypeface(out _GlyphTypeface)) throw new NotSupportedException(); + var map = _GlyphTypeface.CharacterToGlyphMap; + _glyphIndices = _controlChars.Concat(Metrics.Encoding.GetChars(_sweep)).Select(x => { map.TryGetValue(x, out var val); return val; }).ToArray(); + return _GlyphTypeface; } } + ushort[] _glyphIndices; + + /// + /// Convert bytes to glyph indices considering current GlyphTypeface and Encoding + /// + public ushort[] GetGlyphIndices(byte[] data, int start, int count) + { + var result = new ushort[count]; + for (int i = 0; i < count; i++) + result[i] = _glyphIndices[data[start + i]]; + return result; + } + void ComputeCharWidth() { if (LineHeight > 0) diff --git a/Source/Swiddler/Rendering/TextFragment.cs b/Source/Swiddler/Rendering/TextFragment.cs index c7f4d14..561e362 100644 --- a/Source/Swiddler/Rendering/TextFragment.cs +++ b/Source/Swiddler/Rendering/TextFragment.cs @@ -20,20 +20,15 @@ public override void OnRender(DrawingContext drawingContext, Rect bounds) if (Length == 0) return; - ushort[] glyphIndexes = new ushort[Length]; - double[] advanceWidths = new double[Length]; - GlyphTypeface glyphTypeface = View.Content.GlyphTypeface; + + var advanceWidths = new double[Length]; + var glyphIndices = View.Content.GetGlyphIndices(Data, Offset, Length); + double charWidth = View.Content.CharWidth; double fontSize = View.Content.LineHeight - 2; // keep padding - for (int n = 0; n < Length; n++) - { - if (glyphTypeface.CharacterToGlyphMap.TryGetValue(Data[Offset + n], out ushort glyphIndex)) - glyphIndexes[n] = glyphIndex; - - advanceWidths[n] = charWidth; - } + for (int n = 0; n < Length; n++) advanceWidths[n] = charWidth; var baseline = new Point(0, View.Content.SnapToPixelsY(glyphTypeface.Baseline * fontSize + 1.5, ceiling: true)); @@ -41,7 +36,7 @@ public override void OnRender(DrawingContext drawingContext, Rect bounds) bidiLevel: 0, isSideways: false, renderingEmSize: fontSize, - glyphIndices: glyphIndexes, + glyphIndices: glyphIndices, baselineOrigin: baseline, advanceWidths: advanceWidths, glyphOffsets: null, diff --git a/Source/Swiddler/Swiddler.csproj b/Source/Swiddler/Swiddler.csproj index 7c596ff..cef31fc 100644 --- a/Source/Swiddler/Swiddler.csproj +++ b/Source/Swiddler/Swiddler.csproj @@ -89,6 +89,7 @@ + @@ -149,10 +150,16 @@ - + + + + + + + diff --git a/Source/Swiddler/Utils/RtfWriter/RtfAbstract.cs b/Source/Swiddler/Utils/RtfWriter/RtfAbstract.cs new file mode 100644 index 0000000..9d49603 --- /dev/null +++ b/Source/Swiddler/Utils/RtfWriter/RtfAbstract.cs @@ -0,0 +1,57 @@ +namespace Swiddler.Utils.RtfWriter +{ + /// + /// Internal use only. + /// Objects that are renderable can emit RTF code. + /// + abstract public class RtfRenderable + { + /// + /// Internal use only. + /// Emit RTF code. + /// + /// RTF code + abstract public string render(); + } + + /// + /// Internal use only. + /// RtfBlock is a content block that cannot contain other blocks. + /// For example, an image is an RtfBlock because it cannot contain + /// other content block such as another image, a paragraph, a table, + /// etc. + /// + abstract public class RtfBlock : RtfRenderable + { + /// + /// How this block is aligned in its containing block. + /// + abstract public Align Alignment { get; set; } + /// + /// Default character formats. + /// + abstract public RtfCharFormat DefaultCharFormat { get; } + /// + /// When set to true, this block will be arranged in the beginning + /// of a new page. + /// + abstract public bool StartNewPage { get; set; } + + protected string AlignmentCode() + { + switch (Alignment) + { + case Align.Left: + return @"\ql"; + case Align.Right: + return @"\qr"; + case Align.Center: + return @"\qc"; + case Align.FullyJustify: + return @"\qj"; + default: + return @"\qd"; + } + } + } +} diff --git a/Source/Swiddler/Utils/RtfWriter/RtfBasics.cs b/Source/Swiddler/Utils/RtfWriter/RtfBasics.cs new file mode 100644 index 0000000..30e69a2 --- /dev/null +++ b/Source/Swiddler/Utils/RtfWriter/RtfBasics.cs @@ -0,0 +1,574 @@ +using System; +using System.Windows.Media; + +namespace Swiddler.Utils.RtfWriter +{ + /// + /// Horizontal alignment. + /// + public enum Align + { + None = 0, + Left, + Right, + Center, + FullyJustify, + Distributed, + } + + /// + /// Vertical alignment. + /// + public enum AlignVertical + { + Top = 1, + Middle, + Bottom, + } + + /// + /// Top, bottom, left, and right. + /// + public enum Direction + { + Top = 0, + Right, + Bottom, + Left, + } + + /// + /// Types of paper sizes. + /// + public enum PaperSize + { + Letter = 1, + A4, + A3, + } + + /// + /// Types of paper orientaion. + /// + public enum PaperOrientation + { + Portrait = 1, + Landscape, + } + + /// + /// Types of locality. + /// + public enum Lcid + { + TraditionalChinese = 1028, + English = 1033, + French = 1036, + German = 1031, + Italian = 1040, + Japanese = 1041, + Korean = 1042, + SimplifiedChinese = 2052, + Spanish = 3082, + } + + /// + /// Types of font styles. + /// + public enum FontStyleFlag + { + Bold = 0x01, + Italic = 0x02, + Underline = 0x04, + Super = 0x08, + Sub = 0x10, + Scaps = 0x20, + Strike = 0x40, + } + + /// + /// Types of border styles. + /// + public enum BorderStyle + { + None = 0, + Single, + Dotted, + Dashed, + Double, + } + + /// + /// Types of two-in-one style quoting symbols. + /// (For Far East character formatting.) + /// + public enum TwoInOneStyle + { + NotEnabled = 0, + None, + Parentheses, + SquareBrackets, + AngledBrackets, + Braces, + } + + /// + /// Container for a set of font styles. It is helpful when more than + /// one of the font styles (e.g., both bold and italic) are associated with + /// some characters. + /// + public class FontStyle + { + private UInt32 _styleAdd, _styleRemove; + + /// + /// Internal use only. + /// Constructor that initializes as containing none of the styles. + /// + internal FontStyle() + { + _styleAdd = _styleRemove = 0; + } + + /// + /// Internal use only. + /// Copy constructor. + /// + /// + internal FontStyle(FontStyle src) + { + _styleAdd = src._styleAdd; + _styleRemove = src._styleRemove; + } + + /// + /// Add a font style to the set. Adding a font style + /// that is already in the set has no effect. + /// + /// Font style to be added. + public void addStyle(FontStyleFlag sty) + { + _styleAdd |= (UInt32) sty; + _styleRemove &= ~( (UInt32) sty ); + } + + /// + /// Remove a font style from the set. Removing a font style + /// that is already not in the set has no effect. + /// + /// Font style to be removed. + public void removeStyle(FontStyleFlag sty) + { + _styleAdd &= ~( (UInt32) sty ); + _styleRemove |= (UInt32) sty; + } + + /// + /// Test whether a font style is in the set. + /// + /// Font style to be tested. + /// True if the font style is in the set; false otherwise. + public bool containsStyleAdd( FontStyleFlag sty ) + { + if ((_styleAdd & (UInt32) sty) > 0) { + return true; + } + return false; + } + + public bool containsStyleRemove(FontStyleFlag sty) + { + if ((_styleRemove & (UInt32) sty) > 0) { + return true; + } + return false; + } + + /// + /// Indicate whether the set is empty. + /// + public bool IsEmpty + { + get + { + return _styleAdd == 0 && _styleRemove == 0; + } + } + } + + /// + /// A descriptor for a font. Fonts are assigned as descriptors, + /// not names (e.g., Times New Roman). + /// + public class FontDescriptor + { + private int _descr; + + /// + /// Internal use only. + /// Constructor. + /// + /// Internal representative integer of the font. + internal FontDescriptor(int descr) + { + _descr = descr; + } + + /// + /// Internal use only. + /// Get internal representative integer of the font. + /// + internal int Value + { + get + { + return _descr; + } + } + } + + /// + /// A descriptor for a color. Colors are assigned as descriptors, + /// not names (e.g., #ff0000, or RED). + /// + public class ColorDescriptor + { + private int _descr; + + /// + /// Internal use only. + /// Constructor. + /// + /// Internal representative integer for the color. + internal ColorDescriptor(int descr) + { + _descr = descr; + } + + /// + /// Internal use only. + /// Get internal representative integer for the color. + /// + internal int Value + { + get + { + return _descr; + } + } + } + + /// + /// Margin settings for a content block, containing four margin values. + /// + public class Margins + { + private float[] _margins; + + /// + /// Internal use only. + /// Constructor that initializes all four margins as -1. + /// + internal Margins() + { + _margins = new float[4]; + } + + /// + /// Internal use only. + /// Constructor that gives initial values for all four margins. + /// + /// Top margin size in points. + /// Right margin size in points. + /// Bottom margin size in points. + /// Left margin size in points. + internal Margins(float t, float r, float b, float l) + : this() + { + _margins[(int) Direction.Top] = t; + _margins[(int) Direction.Right] = r; + _margins[(int) Direction.Bottom] = b; + _margins[(int) Direction.Left] = l; + } + + /// + /// Indexer that allows getting and setting of one of the four margin values. + /// + /// The direction at which the margin locates. One of top, + /// right, bottom, left. + /// Margin size in points. + public float this[Direction d] + { + get + { + int i = (int) d; + if (i >= 0 && i < _margins.Length) { + return _margins[i]; + } + throw new Exception("Not a valid direction."); + } + set + { + int i = (int)d; + if (i >= 0 && i < _margins.Length) { + _margins[i] = value; + } else { + throw new Exception("Not a valid direction."); + } + } + } + + public bool equals( Margins margins ) + { + return ( margins._margins[(int) Direction.Bottom] == _margins[(int) Direction.Bottom] ) && + ( margins._margins[(int) Direction.Left] == _margins[(int) Direction.Left] ) && + ( margins._margins[(int) Direction.Right] == _margins[(int) Direction.Right] ) && + ( margins._margins[(int) Direction.Top] == _margins[(int) Direction.Top] ); + } + } + + /// + /// Border attributes for table cells. + /// + public class Border + { + private BorderStyle _style; + private float _width; + private ColorDescriptor _colorDesc; + + /// + /// Internal use only. + /// Default constructor that sets border style to None. + /// + internal Border() + { + _style = BorderStyle.None; + _width = 0.5F; + _colorDesc = new ColorDescriptor(0); + } + + /// + /// Indirect use only. + /// See if two borders are equal. + /// + /// Border object to be compared with. + /// True if the two borders are equal; false otherwise. + public override bool Equals(object obj) + { + Border bdr = (Border) obj; + return (this.Style == bdr.Style && this.Width == bdr.Width); + } + + /// + /// Indirect use only. + /// Differentiate borders. + /// + /// A hash code representing different sets of border attributes. + public override int GetHashCode() + { + return _width.GetHashCode() * 1000 + (int) _style; + } + + /// + /// Get or set the border style. + /// + public BorderStyle Style + { + get + { + return _style; + } + set + { + _style = value; + } + } + + /// + /// Get or set the width of the border line. + /// + public float Width + { + get + { + return _width; + } + set + { + _width = value; + } + } + + /// + /// Get or set the border color. + /// + public ColorDescriptor Color + { + get + { + return _colorDesc; + } + set + { + _colorDesc = value; + } + } + } + + /// + /// Border settings for a table cell, containing four sets of border attributes. + /// + public class Borders + { + private Border[] _borders; + + /// + /// Internal use only. + /// Default constructor that sets all border style to None. + /// + internal Borders() + { + _borders = new Border[4]; + for (int i = 0; i < _borders.Length; i++) { + _borders[i] = new Border(); + } + } + + /// + /// Indexer that gets border attributes for borders in any of the four + /// direction. + /// + /// The direction at which the border locates. One of top + /// right, bottom, left. + /// The border attributes. + public Border this[Direction d] + { + get + { + int i = (int)d; + if (i >= 0 && i < _borders.Length) { + return _borders[i]; + } + throw new Exception("Not a valid direction."); + } + } + } + + /// + /// Colors to be applied in the document. Note that objects of this class + /// cannot be assigned to document directly. Instead, they work through + /// ColorDescriptor objects. + /// + public class RtfColor + { + private int _color; + + /// + /// Default constructor that initialized as black color. + /// + public RtfColor() + { + _color = 0; + } + + /// + /// Constructor that initializes using RGB values. + /// + /// Red component of the color. + /// Green component of the color. + /// Blue component of the color. + public RtfColor(byte red, byte green, byte blue) + { + _color = (red << 16) + (green << 8) + blue; + } + + public RtfColor(Color color) : this(color.R, color.G, color.B) { } + + /// + /// Constructor that initializes using a string representation of + /// a hexadecimal value. + /// + /// String representation of a hexadecimal value, such + /// as "FF0000" or "00AB12". + public RtfColor(string hex) + { + if (hex == null || hex.Length != 6) { + throw new Exception("String parameter hex should be of length 6."); + } + hex = hex.ToUpper(); + for (int i = 0; i < hex.Length; i++) { + if (!Char.IsDigit(hex[i]) && (hex[i] < 'A' || hex[i] > 'F')) { + throw new Exception("Characters of parameter hex should be in [0-9,A-F,a-f]"); + } + } + byte red = Convert.ToByte(hex.Substring(0, 2), 16); + byte green = Convert.ToByte(hex.Substring(2, 2), 16); + byte blue = Convert.ToByte(hex.Substring(4, 2), 16); + _color = (red << 16) + (green << 8) + blue; + } + + /// + /// Indirect use only. + /// See if two colors are the same. + /// + /// Color object to be compared with. + /// True if two colors are identical; false otherwise. + public override bool Equals(object obj) + { + RtfColor b = (RtfColor) obj; + return (b._color == this._color); + } + + /// + /// Indirect use only. + /// Differentiate colors. + /// + /// A hash code used to differentiate colors. + public override int GetHashCode() + { + return _color; + } + + /// + /// Get or set the red component of the color. + /// + internal string Red + { + get + { + return ((_color >> 16) % 256).ToString(); + } + } + + /// + /// Get or set the green component of the color. + /// + internal string Green + { + get + { + return ((_color >> 8) % 256).ToString(); + } + } + + /// + /// Get or set the blue component of the color. + /// + internal string Blue + { + get + { + return (_color % 256).ToString(); + } + } + } + + /// + /// Internal use only. + /// Constant values for default document settings. + /// + internal static class DefaultValue + { + public static string Font = "Lucida Console"; + } +} diff --git a/Source/Swiddler/Utils/RtfWriter/RtfBlockList.cs b/Source/Swiddler/Utils/RtfWriter/RtfBlockList.cs new file mode 100644 index 0000000..0a8f68e --- /dev/null +++ b/Source/Swiddler/Utils/RtfWriter/RtfBlockList.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Swiddler.Utils.RtfWriter +{ + /// + /// A container for an array of content blocks. For example, a footnote + /// is a RtfBlockList because it may contains a paragraph and an image. + /// + public class RtfBlockList : RtfRenderable + { + /// + /// Storage for array of content blocks. + /// + protected List _blocks; + /// + /// Default character formats within this container. + /// + protected RtfCharFormat _defaultCharFormat; + + private bool _allowParagraph; + private bool _allowFootnote; + private bool _allowControlWord; + + /// + /// Internal use only. + /// Default constructor that allows containing all types of content blocks. + /// + internal RtfBlockList() + : this(true, true, true) + { + } + + /// + /// Internal use only. + /// Constructor specifying allowed content blocks to be contained. + /// + /// Whether an RtfParagraph is allowed. + internal RtfBlockList(bool allowParagraph) + : this(allowParagraph, true, true) + { + } + + /// + /// Internal use only. + /// Constructor specifying allowed content blocks to be contained. + /// + /// Whether an RtfParagraph is allowed. + /// Whether an RtfFootnote is allowed in contained RtfParagraph. + /// Whether an field control word is allowed in contained RtfParagraph. + internal RtfBlockList(bool allowParagraph, bool allowFootnote, bool allowControlWord) + { + _blocks = new List(); + _allowParagraph = allowParagraph; + _allowFootnote = allowFootnote; + _allowControlWord = allowControlWord; + _defaultCharFormat = null; + } + + /// + /// Get default character formats within this container. + /// + public RtfCharFormat DefaultCharFormat + { + get + { + if (_defaultCharFormat == null) { + _defaultCharFormat = new RtfCharFormat(-1, -1, 1); + } + return _defaultCharFormat; + } + } + + private void addBlock(RtfBlock block) + { + if (block != null) { + _blocks.Add(block); + } + } + + /// + /// Add a paragraph to this container. + /// + /// Paragraph being added. + public RtfParagraph addParagraph() + { + if (!_allowParagraph) { + throw new Exception("Paragraph is not allowed."); + } + RtfParagraph block = new RtfParagraph(_allowFootnote, _allowControlWord); + addBlock(block); + return block; + } + + /// + /// Internal use only. + /// Emit RTF code. + /// + /// Resulting RTF code for this object. + public override string render() + { + StringBuilder result = new StringBuilder(); + + result.AppendLine(); + for (int i = 0; i < _blocks.Count; i++) { + if (_defaultCharFormat != null && _blocks[i].DefaultCharFormat != null) { + _blocks[i].DefaultCharFormat.copyFrom(_defaultCharFormat); + } + result.AppendLine(_blocks[i].render()); + } + return result.ToString(); + } + } +} diff --git a/Source/Swiddler/Utils/RtfWriter/RtfCharFormat.cs b/Source/Swiddler/Utils/RtfWriter/RtfCharFormat.cs new file mode 100644 index 0000000..f5ab081 --- /dev/null +++ b/Source/Swiddler/Utils/RtfWriter/RtfCharFormat.cs @@ -0,0 +1,319 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Text; + +namespace Swiddler.Utils.RtfWriter +{ + /// + /// Summary description for RtfCharFormat + /// + public class RtfCharFormat + { + private int _begin; + private int _end; + private FontDescriptor _font; + private FontDescriptor _ansiFont; + private float _fontSize; + private FontStyle _fontStyle; + private ColorDescriptor _bgColor; + private ColorDescriptor _fgColor; + private TwoInOneStyle _twoInOneStyle; + private string _bookmark; + private string _localHyperlink; + private string _localHyperlinkTip; + + + internal RtfCharFormat(int begin, int end, int textLength) + { + // Note: + // In the condition that ``_begin == _end == -1'', + // the character formatting is applied to the whole paragraph. + _begin = -1; + _end = -1; + _font = null; // do not specify font (use default one) + _ansiFont = null; // do not specify font (use default one) + _fontSize = -1; // do not specify font size (use default one) + _fontStyle = new FontStyle(); + _bgColor = null; + _fgColor = null; + _twoInOneStyle = TwoInOneStyle.NotEnabled; + _bookmark = ""; + setRange(begin, end, textLength); + } + + internal void copyFrom(RtfCharFormat src) + { + if (src == null) { + return; + } + _begin = src._begin; + _end = src._end; + if (_font == null && src._font != null) { + _font = new FontDescriptor(src._font.Value); + } + if (_ansiFont == null && src._ansiFont != null) { + _ansiFont = new FontDescriptor(src._ansiFont.Value); + } + if (_fontSize < 0 && src._fontSize >= 0) { + _fontSize = src._fontSize; + } + if (_fontStyle.IsEmpty && !src._fontStyle.IsEmpty) { + _fontStyle = new FontStyle(src._fontStyle); + } + if (_bgColor == null && src._bgColor != null) { + _bgColor = new ColorDescriptor(src._bgColor.Value); + } + if (_fgColor == null && src._fgColor != null) { + _fgColor = new ColorDescriptor(src._fgColor.Value); + } + } + + private void setRange(int begin, int end, int textLength) + { + if (begin > end) { + throw new Exception("Invalid range: (" + begin + ", " + end + ")"); + } else if (begin < 0 || end < 0) { + if (begin != -1 || end != -1) { + throw new Exception("Invalid range: (" + begin + ", " + end + ")"); + } + } + if (end >= textLength) { + throw new Exception("Range ending out of range: " + end); + } + _begin = begin; + _end = end; + } + + internal int Begin + { + get + { + return _begin; + } + } + + internal int End + { + get + { + return _end; + } + } + + public string Bookmark + { + get + { + return _bookmark; + } + set + { + _bookmark = value; + } + } + + public string LocalHyperlink + { + get + { + return _localHyperlink; + } + set + { + _localHyperlink = value; + } + } + + public string LocalHyperlinkTip + { + get + { + return _localHyperlinkTip; + } + set + { + _localHyperlinkTip = value; + } + } + + public FontDescriptor Font + { + get + { + return _font; + } + set + { + _font = value; + } + } + + public FontDescriptor AnsiFont + { + get + { + return _ansiFont; + } + set + { + _ansiFont = value; + } + } + + public float FontSize + { + get + { + return _fontSize; + } + set + { + _fontSize = value; + } + } + + public FontStyle FontStyle + { + get + { + return _fontStyle; + } + } + + public ColorDescriptor FgColor + { + get + { + return _fgColor; + } + set + { + _fgColor = value; + } + } + + public ColorDescriptor BgColor + { + get + { + return _bgColor; + } + set + { + _bgColor = value; + } + } + + public TwoInOneStyle TwoInOneStyle + { + get + { + return _twoInOneStyle; + } + set + { + _twoInOneStyle = value; + } + } + + internal string renderHead() + { + StringBuilder result = new StringBuilder("{"); + + if (!string.IsNullOrEmpty(_localHyperlink)) { + result.Append(@"{\field{\*\fldinst HYPERLINK \\l "); + result.Append("\"" + _localHyperlink + "\""); + if (!string.IsNullOrEmpty(_localHyperlinkTip)) result.Append(" \\\\o \"" + _localHyperlinkTip + "\""); + result.Append(@"}{\fldrslt{"); + } + + + if (_font != null || _ansiFont != null) { + if (_font == null) { + result.Append(@"\f" + _ansiFont.Value); + } else if (_ansiFont == null) { + result.Append(@"\f" + _font.Value); + } else { + result.Append(@"\loch\af" + _ansiFont.Value + @"\hich\af" + _ansiFont.Value + + @"\dbch\af" + _font.Value); + } + } + if (_fontSize > 0) { + result.Append(@"\fs" + RtfUtility.pt2HalfPt(_fontSize)); + } + if (_fgColor != null) { + result.Append(@"\cf" + _fgColor.Value); + } + if (_bgColor != null) { + result.Append(@"\chshdng0\chcbpat" + _bgColor.Value + @"\cb" + _bgColor.Value); + } + + foreach(var fontStyle in _fontStyleMap) + { + if (FontStyle.containsStyleAdd(fontStyle.Key)) { + result.Append(@"\" + fontStyle.Value); + } else if(FontStyle.containsStyleRemove(fontStyle.Key)) { + result.Append(@"\" + fontStyle.Value + "0"); + } + } + if (_twoInOneStyle != TwoInOneStyle.NotEnabled) { + result.Append(@"\twoinone"); + switch (_twoInOneStyle) { + case TwoInOneStyle.None: + result.Append("0"); + break; + case TwoInOneStyle.Parentheses: + result.Append("1"); + break; + case TwoInOneStyle.SquareBrackets: + result.Append("2"); + break; + case TwoInOneStyle.AngledBrackets: + result.Append("3"); + break; + case TwoInOneStyle.Braces: + result.Append("4"); + break; + } + } + + if (result.ToString().Contains(@"\")) { + result.Append(" "); + } + + if (!string.IsNullOrEmpty(_bookmark)) { + result.Append(@"{\*\bkmkstart " + _bookmark + "}"); + } + + return result.ToString(); + } + + internal string renderTail() + { + StringBuilder result = new StringBuilder(""); + + if (!string.IsNullOrEmpty(_bookmark)) { + result.Append(@"{\*\bkmkend " + _bookmark + "}"); + } + + if (!string.IsNullOrEmpty(_localHyperlink)) { + result.Append(@"}}}"); + } + + result.Append("}"); + return result.ToString(); + } + + private static IDictionary _fontStyleMap = new Dictionary + { + {FontStyleFlag.Bold, "b"}, + {FontStyleFlag.Italic, "i"}, + {FontStyleFlag.Scaps, "scaps"}, + {FontStyleFlag.Strike, "strike"}, + {FontStyleFlag.Sub, "sub"}, + {FontStyleFlag.Super, "super"}, + {FontStyleFlag.Underline, "ul"} + }; + } +} diff --git a/Source/Swiddler/Utils/RtfWriter/RtfDocument.cs b/Source/Swiddler/Utils/RtfWriter/RtfDocument.cs new file mode 100644 index 0000000..77631b8 --- /dev/null +++ b/Source/Swiddler/Utils/RtfWriter/RtfDocument.cs @@ -0,0 +1,95 @@ +using System.Collections.Generic; +using System.Text; + +namespace Swiddler.Utils.RtfWriter +{ + /// + /// Summary description for RtfDocument + /// + public class RtfDocument : RtfBlockList + { + private List _fontTable; + private List _colorTable; + + public RtfDocument() + { + _fontTable = new List(); + _fontTable.Add(DefaultValue.Font); + _colorTable = new List(); + _colorTable.Add(new RtfColor()); + } + + public FontDescriptor createFont(string fontName) + { + if (_fontTable.Contains(fontName)) { + return new FontDescriptor(_fontTable.IndexOf(fontName)); + } + _fontTable.Add(fontName); + return new FontDescriptor(_fontTable.IndexOf(fontName)); + } + + public ColorDescriptor createColor(RtfColor color) + { + if (_colorTable.Contains(color)) { + return new ColorDescriptor(_colorTable.IndexOf(color)); + } + _colorTable.Add(color); + return new ColorDescriptor(_colorTable.IndexOf(color)); + } + + public override string render() + { + StringBuilder rtf = new StringBuilder(); + + // --------------------------------------------------- + // Prologue + // --------------------------------------------------- + rtf.AppendLine(@"{\rtf1\ansi\deff0"); + rtf.AppendLine(); + + // --------------------------------------------------- + // Insert font table + // --------------------------------------------------- + rtf.AppendLine(@"{\fonttbl"); + for (int i = 0; i < _fontTable.Count; i++) { + rtf.AppendLine(@"{\f" + i + " " + RtfUtility.unicodeEncode(_fontTable[i].ToString()) + ";}"); + } + rtf.AppendLine("}"); + rtf.AppendLine(); + + // --------------------------------------------------- + // Insert color table + // --------------------------------------------------- + rtf.AppendLine(@"{\colortbl"); + rtf.AppendLine(";"); + for (int i = 1; i < _colorTable.Count; i++) { + RtfColor c = _colorTable[i]; + rtf.AppendLine(@"\red" + c.Red + @"\green" + c.Green + @"\blue" + c.Blue + ";"); + } + rtf.AppendLine("}"); + rtf.AppendLine(); + /* + + // --------------------------------------------------- + // Preliminary + // --------------------------------------------------- + rtf.AppendLine(@"\deflang" + (int)_lcid + @"\plain\fs" + + RtfUtility.pt2HalfPt(DefaultValue.FontSize) + @"\widowctrl\hyphauto\ftnbj"); + */ + + rtf.AppendLine(); + + // --------------------------------------------------- + // Document body + // --------------------------------------------------- + rtf.Append(base.render()); + + // --------------------------------------------------- + // Ending + // --------------------------------------------------- + rtf.AppendLine("}"); + + return rtf.ToString(); + } + } +} diff --git a/Source/Swiddler/Utils/RtfWriter/RtfParagraph.cs b/Source/Swiddler/Utils/RtfWriter/RtfParagraph.cs new file mode 100644 index 0000000..0e0f40d --- /dev/null +++ b/Source/Swiddler/Utils/RtfWriter/RtfParagraph.cs @@ -0,0 +1,394 @@ +using System; +using System.Configuration; +using System.Collections.Generic; +using System.Text; + +namespace Swiddler.Utils.RtfWriter +{ + /// + /// Summary description for RtfParagraph + /// + public class RtfParagraph : RtfBlock + { + private StringBuilder _text; + private float _linespacing; + private Margins _margins; + private Align _align; + private List _charFormats; + protected bool _allowFootnote; + protected bool _allowControlWord; + private string _blockHead; + private string _blockTail; + private bool _startNewPage; + private float _firstLineIndent; + private RtfCharFormat _defaultCharFormat; + + protected struct Token + { + public string text; + public bool isControl; + } + + private class DisjointRange + { + public DisjointRange() + { + head = -1; + tail = -1; + format = null; + } + public int head; + public int tail; + public RtfCharFormat format; + } + + public RtfParagraph() + : this(false, false) + { + } + + public RtfParagraph(bool allowFootnote, bool allowControlWord) + { + _text = new StringBuilder(); + _linespacing = -1; + _margins = new Margins(); + _align = Align.Left; //Changed default to .Left as .None was spreading text accross page. + _charFormats = new List(); + _allowFootnote = allowFootnote; + _allowControlWord = allowControlWord; + _blockHead = @"{\pard"; + _blockTail = @"\par}"; + _startNewPage = false; + _firstLineIndent = 0; + _defaultCharFormat = null; + } + + public StringBuilder Text + { + get + { + return _text; + } + } + + public float LineSpacing + { + get + { + return _linespacing; + } + set + { + _linespacing = value; + } + } + + public float FirstLineIndent + { + get + { + return _firstLineIndent; + } + set + { + _firstLineIndent = value; + } + } + + public void setText(string text) + { + _text = new StringBuilder(text); + } + + public override RtfCharFormat DefaultCharFormat + { + get + { + if (_defaultCharFormat == null) { + _defaultCharFormat = new RtfCharFormat(-1, -1, _text.Length); + } + return _defaultCharFormat; + } + } + + public override bool StartNewPage + { + get + { + return _startNewPage; + } + set + { + _startNewPage = value; + } + } + + public override Align Alignment + { + get + { + return _align; + } + set + { + _align = value; + } + } + + /// + /// Add a character formatting to a range in this paragraph. + /// To specify the whole paragraph as the range, set begin = end = -1. + /// Format that is added latter will override the former, if their + /// range overlays each other. + /// + /// Beginning of the range + /// End of the range + public RtfCharFormat addCharFormat(int begin, int end) + { + RtfCharFormat fmt = new RtfCharFormat(begin, end, _text.Length); + _charFormats.Add(fmt); + return fmt; + } + + public RtfCharFormat addCharFormat() + { + return addCharFormat(-1, -1); + } + + protected LinkedList buildTokenList() + { + int count; + Token token; + LinkedList tokList = new LinkedList(); + LinkedListNode node; + List dranges = new List(); + + #region Build head[] and tail[] from char format range for later use. + // -------------------------------------------------- + // Transform possibly overlapped character format ranges into + // disjoint ranges. + // -------------------------------------------------- + for (int i = 0; i < _charFormats.Count; i++) { + RtfCharFormat fmt = _charFormats[i]; + DisjointRange range = null; + if (fmt.Begin == -1 && fmt.End == -1) { + range = new DisjointRange(); + range.head = 0; + range.tail = _text.Length - 1; + range.format = fmt; + } else if (fmt.Begin <= fmt.End) { + range = new DisjointRange(); + range.head = fmt.Begin; + range.tail = fmt.End; + range.format = fmt; + } else { + continue; + } + if (range.tail >= _text.Length) { + range.tail = _text.Length - 1; + if (range.head > range.tail) { + continue; + } + } + // make the ranges disjoint from each other. + List delList = new List(); + List addList = new List(); + List addAnchorList = new List(); + for (int j = 0; j < dranges.Count; j++) { + DisjointRange r = dranges[j]; + if (range.head <= r.head && range.tail >= r.tail) { + // former range is totally covered by the later + // |--------| r + // |-----------------| range + delList.Add(r); + } else if (range.head <= r.head && range.tail >= r.head && range.tail < r.tail) { + // former range is partially covered + // |------------------| r + // |-----------------| range + r.head = range.tail + 1; + } else if (range.head > r.head && range.head <= r.tail && range.tail >= r.tail) { + // former range is partially covered + // |------------------| r + // |-----------------| range + r.tail = range.head - 1; + } else if (range.head > r.head && range.tail < r.tail) { + // later range is totally covered by the former + // |----------------------| r + // |---------| range + DisjointRange newRange = new DisjointRange(); + newRange.head = range.tail + 1; + newRange.tail = r.tail; + newRange.format = r.format; + r.tail = range.head - 1; + addList.Add(newRange); + addAnchorList.Add(r); + } + } + dranges.Add(range); + for (int j = 0; j < delList.Count; j++) { + dranges.Remove(delList[j]); + } + for (int j = 0; j < addList.Count; j++) { + int index = dranges.IndexOf(addAnchorList[j]); + if (index < 0) { + continue; + } + dranges.Insert(index, addList[j]); + } + } + #endregion + token = new Token(); + token.text = _text.ToString(); + token.isControl = false; + tokList.AddLast(token); + #region Build token list from head[] and tail[]. + // -------------------------------------------------- + // Build token list from head[] and tail[]. + // -------------------------------------------------- + for (int i = 0; i < dranges.Count; i++) { + DisjointRange r = dranges[i]; + count = 0; + // process head[i] + if (r.head == 0) { + Token newTok = new Token(); + newTok.isControl = true; + newTok.text = r.format.renderHead(); + tokList.AddFirst(newTok); + } else { + node = tokList.First; + while (node != null) { + Token tok = node.Value; + + if (!tok.isControl) { + count += tok.text.Length; + if (count == r.head) { + Token newTok = new Token(); + newTok.isControl = true; + newTok.text = r.format.renderHead(); + while (node.Next != null && node.Next.Value.isControl) { + node = node.Next; + } + tokList.AddAfter(node, newTok); + break; + } else if (count > r.head) { + LinkedListNode newNode; + Token newTok1 = new Token(); + newTok1.isControl = false; + newTok1.text = tok.text.Substring(0, tok.text.Length - (count - r.head)); + newNode = tokList.AddAfter(node, newTok1); + Token newTok2 = new Token(); + newTok2.isControl = true; + newTok2.text = r.format.renderHead(); + newNode = tokList.AddAfter(newNode, newTok2); + Token newTok3 = new Token(); + newTok3.isControl = false; + newTok3.text = tok.text.Substring(tok.text.Length - (count - r.head)); + newNode = tokList.AddAfter(newNode, newTok3); + tokList.Remove(node); + break; + } + } + node = node.Next; + } + } + // process tail[i] + count = 0; + node = tokList.First; + while (node != null) { + Token tok = node.Value; + + if (!tok.isControl) { + count += tok.text.Length; + if (count - 1 == r.tail) { + Token newTok = new Token(); + newTok.isControl = true; + newTok.text = r.format.renderTail(); + tokList.AddAfter(node, newTok); + break; + } else if (count - 1 > r.tail) { + LinkedListNode newNode; + Token newTok1 = new Token(); + newTok1.isControl = false; + newTok1.text = tok.text.Substring(0, tok.text.Length - (count - r.tail) + 1); + newNode = tokList.AddAfter(node, newTok1); + Token newTok2 = new Token(); + newTok2.isControl = true; + newTok2.text = r.format.renderTail(); + newNode = tokList.AddAfter(newNode, newTok2); + Token newTok3 = new Token(); + newTok3.isControl = false; + newTok3.text = tok.text.Substring(tok.text.Length - (count - r.tail) + 1); + newNode = tokList.AddAfter(newNode, newTok3); + tokList.Remove(node); + break; + } + } + node = node.Next; + } + } // end for each char format + #endregion + + return tokList; + } + + protected string extractTokenList(LinkedList tokList) + { + LinkedListNode node; + StringBuilder result = new StringBuilder(); + + node = tokList.First; + while (node != null) { + if (node.Value.isControl) { + result.Append(node.Value.text); + } else { + result.Append(RtfUtility.unicodeEncode(node.Value.text)); + } + node = node.Next; + } + return result.ToString(); + } + + public override string render() + { + LinkedList tokList = buildTokenList(); + StringBuilder result = new StringBuilder(_blockHead); + + if (_startNewPage) { + result.Append(@"\pagebb"); + } + + if (_linespacing >= 0) { + result.Append(@"\sl-" + RtfUtility.pt2Twip(_linespacing) + @"\slmult0"); + } + if (_margins[Direction.Top] > 0) { + result.Append(@"\sb" + RtfUtility.pt2Twip(_margins[Direction.Top])); + } + if (_margins[Direction.Bottom] > 0) { + result.Append(@"\sa" + RtfUtility.pt2Twip(_margins[Direction.Bottom])); + } + if (_margins[Direction.Left] > 0) { + result.Append(@"\li" + RtfUtility.pt2Twip(_margins[Direction.Left])); + } + if (_margins[Direction.Right] > 0) { + result.Append(@"\ri" + RtfUtility.pt2Twip(_margins[Direction.Right])); + } + //if (_firstLineIndent != 0) { + result.Append(@"\fi" + RtfUtility.pt2Twip(_firstLineIndent)); + //} + result.Append(AlignmentCode()); + result.AppendLine(); + + // insert default char format intto the 1st position of _charFormats + if (_defaultCharFormat != null) { + result.AppendLine(_defaultCharFormat.renderHead()); + } + result.AppendLine(extractTokenList(tokList)); + if (_defaultCharFormat != null) { + result.Append(_defaultCharFormat.renderTail()); + } + + result.AppendLine(_blockTail); + return result.ToString(); + } + } +} diff --git a/Source/Swiddler/Utils/RtfWriter/RtfUtility.cs b/Source/Swiddler/Utils/RtfWriter/RtfUtility.cs new file mode 100644 index 0000000..af73e77 --- /dev/null +++ b/Source/Swiddler/Utils/RtfWriter/RtfUtility.cs @@ -0,0 +1,91 @@ +using System; +using System.Text; + +namespace Swiddler.Utils.RtfWriter +{ + /// + /// Summary description for RtfUtility + /// + public static class RtfUtility + { + public static float mm2Points( float mm ) + { + return mm * (float) 2.836; + } + + public static int mm2Twips( float mm ) + { + var inches = mm * 0.0393700787; + return Convert.ToInt32( inches * 1440 ); + } + + public static int pt2Twip(float pt) + { + return !float.IsNaN( pt ) ? Convert.ToInt32( pt * 20 ) : 0; + } + + public static int pt2HalfPt(float pt) + { + return Convert.ToInt32(pt * 2); + } + + public static string unicodeEncode(string str) + { + StringBuilder result = new StringBuilder(); + int unicode; + + for (int i = 0; i < str.Length; i++) { + unicode = (int)str[i]; + if (str[i] == '\n') { + result.AppendLine(@"\line"); + } else if (str[i] == '\r') { + // ignore '\r' + } else if (str[i] == '\t') { + result.Append(@"\tab "); + } else if (unicode <= 0xff) { + if (unicode == 0x5c || unicode == 0x7b || unicode == 0x7d) { + result.Append(@"\'" + string.Format("{0:x2}", unicode)); + } else if (0x00 <= unicode && unicode < 0x20) { + result.Append(@"\'" + string.Format("{0:x2}", unicode)); + } else if (0x20 <= unicode && unicode < 0x80) { + result.Append(str[i]); + } else { // 0x80 <= unicode <= 0xff + result.Append(@"\'" + string.Format("{0:x2}", unicode)); + } + } else if (0xff < unicode && unicode <= 0x8000) { + result.Append(@"\uc1\u" + unicode + "*"); + } else if (0x8000 < unicode && unicode <= 0xffff) { + result.Append(@"\uc1\u" + (unicode - 0x10000) + "*"); + } else { + result.Append(@"\uc1\u9633*"); + } + } + return result.ToString(); + } + + /// + /// big5 encoding (preserve this function for failure restoration) + /// + /// string to be encoded + /// encoded string + public static string big5Encode(string str) + { + string result = ""; + Encoding big5 = Encoding.GetEncoding(950); + Encoding ascii = Encoding.ASCII; + Byte[] buf = big5.GetBytes(str); + Byte c; + + for (int i = 0; i < buf.Length; i++) { + c = buf[i]; + if ((0x00 <= c && c < 0x20) || (0x80 <= c && c <= 0xff) + || c == 0x5c || c == 0x7b || c == 0x7d) { + result += string.Format(@"\'{0:x2}", c); + } else { + result += ascii.GetChars(new byte[] { c })[0]; + } + } + return result; + } + } +} diff --git a/Source/Swiddler/Views/MainWindow.xaml b/Source/Swiddler/Views/MainWindow.xaml index 7e80884..5d9fd7e 100644 --- a/Source/Swiddler/Views/MainWindow.xaml +++ b/Source/Swiddler/Views/MainWindow.xaml @@ -169,6 +169,7 @@ - + @@ -102,7 +102,7 @@ - + diff --git a/Source/Swiddler/Views/NewConnection.xaml.cs b/Source/Swiddler/Views/NewConnection.xaml.cs index 519febc..0a72fb1 100644 --- a/Source/Swiddler/Views/NewConnection.xaml.cs +++ b/Source/Swiddler/Views/NewConnection.xaml.cs @@ -44,12 +44,12 @@ public NewConnection() } else { - Width = 700; + Width = 700; Height = 500; } var history = ConnectionSettings.GetHistory(); - var firstConn = history.FirstOrDefault(); + var firstConn = history.FirstOrDefault()?.Copy(); clipboardConnectionSettings = TryCreateFromClipboard(firstConn); ConnectionSettings = firstConn ?? QuickActionItem.DefaultTemplates[0].GetConnectionSettings(null); diff --git a/Source/Swiddler/Views/SslHandshakeDetails.xaml b/Source/Swiddler/Views/SslHandshakeDetails.xaml index a1469fa..77b1952 100644 --- a/Source/Swiddler/Views/SslHandshakeDetails.xaml +++ b/Source/Swiddler/Views/SslHandshakeDetails.xaml @@ -17,7 +17,7 @@ WindowStyle="SingleBorderWindow" ResizeMode="CanResize" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" - Height="710" Width="900" + Height="750" Width="900" ShowInTaskbar="False" KeyDown="Window_KeyDown"> -