■ RTF 파일을 생성하는 방법을 보여준다.
[TestLibrary 프로젝트]
▶ BorderStyle.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
namespace TestLibrary { /// <summary> /// 테두리 스타일 /// </summary> public enum BorderStyle { /// <summary> /// 없음 /// </summary> None = 0, /// <summary> /// 단일선 /// </summary> Single, /// <summary> /// 점선 /// </summary> Dotted, /// <summary> /// 대시선 /// </summary> Dashed, /// <summary> /// 이중선 /// </summary> Double } } |
▶ Direction.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
namespace TestLibrary { /// <summary> /// 방향 /// </summary> public enum Direction { /// <summary> /// 위쪽 /// </summary> Top = 0, /// <summary> /// 오른쪽 /// </summary> Right, /// <summary> /// 아래쪽 /// </summary> Bottom, /// <summary> /// 왼쪽 /// </summary> Left } } |
▶ FontStyleFlag.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
namespace TestLibrary { /// <summary> /// 폰트 스타일 플래그 /// </summary> public enum FontStyleFlag { /// <summary> /// 볼드체 /// </summary> Bold = 0x01, /// <summary> /// 이탤릭체 /// </summary> Italic = 0x02, /// <summary> /// 밑줄 /// </summary> Underline = 0x04, /// <summary> /// 위 첨자 /// </summary> Super = 0x08, /// <summary> /// 아래 첨자 /// </summary> Sub = 0x10, /// <summary> /// 작은 대문자 /// </summary> Scaps = 0x20, /// <summary> /// 취소선 /// </summary> Strike = 0x40 } } |
▶ HeaderFooterType.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
namespace TestLibrary { /// <summary> /// 헤더/바닥글 타입 /// </summary> internal enum HeaderFooterType { /// <summary> /// 헤더 /// </summary> Header = 1, /// <summary> /// 바닥글 /// </summary> Footer } } |
▶ HorizontalAlignment.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
namespace TestLibrary { /// <summary> /// 수평 정렬 /// </summary> public enum HorizontalAlignment { /// <summary> /// 해당 무 /// </summary> None = 0, /// <summary> /// 왼쪽 /// </summary> Left, /// <summary> /// 오른쪽 /// </summary> Right, /// <summary> /// 가운데 /// </summary> Center, /// <summary> /// 맞춤 정렬 /// </summary> FullyJustify, /// <summary> /// 분산 /// </summary> Distributed } } |
▶ ImageFileType.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
namespace TestLibrary { /// <summary> /// 이미지 파일 타입 /// </summary> public enum ImageFileType { /// <summary> /// JPEG /// </summary> JPEG = 1, /// <summary> /// GIF /// </summary> GIF, /// <summary> /// PNG /// </summary> PNG } } |
▶ LCID.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
namespace TestLibrary { /// <summary> /// 지역 유형 /// </summary> public enum LCID { /// <summary> /// 중국어 번체 /// </summary> TraditionalChinese = 1028, /// <summary> /// 영어 /// </summary> English = 1033, /// <summary> /// 프랑스어 /// </summary> French = 1036, /// <summary> /// 독일어 /// </summary> German = 1031, /// <summary> /// 이탈리아어 /// </summary> Italian = 1040, /// <summary> /// 일본어 /// </summary> Japanese = 1041, /// <summary> /// 한국어 /// </summary> Korean = 1042, /// <summary> /// 중국어 간체 /// </summary> SimplifiedChinese = 2052, /// <summary> /// 스페인어 /// </summary> Spanish = 3082 } } |
▶ PaperOrientation.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
namespace TestLibrary { /// <summary> /// 용지 방향 /// </summary> public enum PaperOrientation { /// <summary> /// 초상화 /// </summary> Portrait = 1, /// <summary> /// 풍경 /// </summary> Landscape } } |
▶ PaperSize.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
namespace TestLibrary { /// <summary> /// 용지 크기 /// </summary> public enum PaperSize { /// <summary> /// 레터 /// </summary> Letter = 1, /// <summary> /// A4 /// </summary> A4, /// <summary> /// A3 /// </summary> A3 } } |
▶ SectionType.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
namespace TestLibrary { /// <summary> /// 섹션 타입 /// </summary> public enum SectionType { /// <summary> /// 시작 /// </summary> Start, /// <summary> /// 종료 /// </summary> End } } |
▶ TwoInOneStyle.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
namespace TestLibrary { /// <summary> /// 투인원 스타일 /// </summary> /// <remarks> /// 투인원 스타일 인용 기호의 유형 (극동 문자 서식의 경우) /// </remarks> public enum TwoInOneStyle { /// <summary> /// 비활성화 /// </summary> Disabled = 0, /// <summary> /// 해당무 /// </summary> None, /// <summary> /// 괄호 /// </summary> Parentheses, /// <summary> /// 대괄호 /// </summary> SquareBrackets, /// <summary> /// 앵글 브래킷 /// </summary> AngledBrackets, /// <summary> /// 브레이스 /// </summary> Braces } } |
▶ VerticalAlignment.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
namespace TestLibrary { /// <summary> /// 수직 정렬 /// </summary> public enum VerticalAlignment { /// <summary> /// 상단 /// </summary> Top = 1, /// <summary> /// 중단 /// </summary> Middle, /// <summary> /// 하단 /// </summary> Bottom } } |
▶ Border.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
namespace TestLibrary { /// <summary> /// 테두리 /// </summary> public class Border { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 테두리 스타일 /// </summary> private BorderStyle style; /// <summary> /// 테두리 너비 /// </summary> private float width; /// <summary> /// 테두리 색상 설명자 /// </summary> private ColorDescriptor colorDescriptor; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 테두리 스타일 - Style /// <summary> /// 테두리 스타일 /// </summary> public BorderStyle Style { get { return this.style; } set { this.style = value; } } #endregion #region 테두리 너비 - Width /// <summary> /// 테두리 너비 /// </summary> public float Width { get { return this.width; } set { this.width = value; } } #endregion #region 테두리 색상 설명자 - ColorDescriptor /// <summary> /// 테두리 색상 설명자 /// </summary> public ColorDescriptor ColorDescriptor { get { return this.colorDescriptor; } set { this.colorDescriptor = value; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 생성자 - Border() /// <summary> /// 생성자 /// </summary> internal Border() { this.style = BorderStyle.None; this.width = 0.5f; this.colorDescriptor = new ColorDescriptor(0); } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 해시 코드 구하기 - GetHashCode() /// <summary> /// 해시 코드 구하기 /// </summary> /// <returns>해시 코드</returns> public override int GetHashCode() { return this.width.GetHashCode() * 1000 + (int) this.style; } #endregion #region 동일 여부 구하기 - Equals(source) /// <summary> /// 동일 여부 구하기 /// </summary> /// <param name="source">소스 객체</param> /// <returns>동일 여부</returns> public override bool Equals(object source) { Border sourceBorder = (Border)source; return (Style == sourceBorder.Style && Width == sourceBorder.Width); } #endregion } } |
▶ Borders.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
using System; namespace TestLibrary { /// <summary> /// 테두리들 /// </summary> public class Borders { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 테두리 배열 /// </summary> private Border[] borderArray; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 인덱서 - this[direction] /// <summary> /// 인덱서 /// </summary> /// <param name="direction">방향</param> /// <returns>테두리</returns> public Border this[Direction direction] { get { int i = (int)direction; if(i >= 0 && i < this.borderArray.Length) { return this.borderArray[i]; } throw new Exception("Not a valid direction."); } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 생성자 - Borders() /// <summary> /// 생성자 /// </summary> internal Borders() { this.borderArray = new Border[4]; for(int i = 0; i < this.borderArray.Length; i++) { this.borderArray[i] = new Border(); } } #endregion } } |
▶ CellMergeInfo.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
namespace TestLibrary { /// <summary> /// 셀 마진 정보 /// </summary> internal class CellMergeInfo { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 행 인덱스 /// </summary> private int rowIndex; /// <summary> /// 행 범위 /// </summary> private int rowSpan; /// <summary> /// 열 인덱스 /// </summary> private int columnIndex; /// <summary> /// 열 범위 /// </summary> private int columnSpan; /// <summary> /// 대표 셀 /// </summary> private RTFTableCell representativeCell; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 행 인덱스 - RowIndex /// <summary> /// 행 인덱스 /// </summary> internal int RowIndex { get { return this.rowIndex; } } #endregion #region 행 범위 - RowSpan /// <summary> /// 행 범위 /// </summary> internal int RowSpan { get { return this.rowSpan; } } #endregion #region 열 인덱스 - ColumnIndex /// <summary> /// 열 인덱스 /// </summary> internal int ColumnIndex { get { return this.columnIndex; } } #endregion #region 열 범위 - ColumnSpan /// <summary> /// 열 범위 /// </summary> internal int ColumnSpan { get { return this.columnSpan; } } #endregion #region 대표 셀 - RepresentativeCell /// <summary> /// 대표 셀 /// </summary> internal RTFTableCell RepresentativeCell { get { return this.representativeCell; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 생성자 - CellMergeInfo(rowIndex, rowSpan, columnIndex, columnSpan, representativeCell) /// <summary> /// 생성자 /// </summary> /// <param name="rowIndex">행 인덱스</param> /// <param name="rowSpan">행 범위</param> /// <param name="columnIndex">열 인덱스</param> /// <param name="columnSpan">열 범위</param> /// <param name="representativeCell">대표 셀</param> internal CellMergeInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan, RTFTableCell representativeCell) { this.rowIndex = rowIndex; this.rowSpan = rowSpan; this.columnIndex = columnIndex; this.columnSpan = columnSpan; this.representativeCell = representativeCell; } #endregion } } |
▶ ColorDescriptor.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
namespace TestLibrary { /// <summary> /// 색상 설명자 /// </summary> public class ColorDescriptor { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 값 /// </summary> private int value; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 값 - Value /// <summary> /// 값 /// </summary> internal int Value { get { return this.value; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 생성자 - ColorDescriptor(value) /// <summary> /// 생성자 /// </summary> /// <param name="value">값</param> internal ColorDescriptor(int value) { this.value = value; } #endregion } } |
▶ DefaultValue.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
namespace TestLibrary { /// <summary> /// 디폴트 값 /// </summary> internal static class DefaultValue { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Public #region Field /// <summary> /// 폰트 크기 /// </summary> public static int FontSize = 12; /// <summary> /// 폰트 /// </summary> public static string Font = "Times New Roman"; /// <summary> /// 큰 마진 /// </summary> /// <remarks>A4의 긴 가장자리에 사용됨(기존 90)</remarks> public static float LargeMargin = 50; /// <summary> /// 작은 마진 /// </summary> /// <remarks>A4의 짧은 가장자리에 사용됨(기존 72)</remarks> public static float SmallMargin = 50; #endregion } } |
▶ FontDescriptor.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
namespace TestLibrary { /// <summary> /// 폰트 설명자 /// </summary> public class FontDescriptor { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 값 /// </summary> private int value; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 값 - Value /// <summary> /// 값 /// </summary> internal int Value { get { return this.value; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 생성자 - FontDescriptor(value) /// <summary> /// 생성자 /// </summary> /// <param name="value">값</param> internal FontDescriptor(int value) { this.value = value; } #endregion } } |
▶ FontStyle.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
namespace TestLibrary { /// <summary> /// 폰트 스타일 /// </summary> public class FontStyle { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 추가할 스타일 /// </summary> private uint styleToAdd; /// <summary> /// 제거할 스타일 /// </summary> private uint styleToRemove; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 비어있는지 여부 - IsEmpty /// <summary> /// 비어있는지 여부 /// </summary> public bool IsEmpty { get { return this.styleToAdd == 0 && this.styleToRemove == 0; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 생성자 - FontStyle() /// <summary> /// 생성자 /// </summary> internal FontStyle() { this.styleToAdd = 0; this.styleToRemove = 0; } #endregion #region 생성자 - FontStyle(source) /// <summary> /// 생성자 /// </summary> /// <param name="source">소스 폰트 스타일</param> internal FontStyle(FontStyle source) { this.styleToAdd = source.styleToAdd; this.styleToRemove = source.styleToRemove; } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Public #region 스타일 추가하기 - AddStyle(fontStyleFlag) /// <summary> /// 스타일 추가하기 /// </summary> /// <param name="fontStyleFlag">폰트 스타일 플래그</param> public void AddStyle(FontStyleFlag fontStyleFlag) { this.styleToAdd |= (uint)fontStyleFlag; this.styleToRemove &= ~((uint)fontStyleFlag); } #endregion #region 스타일 제거하기 - RemoveStyle(fontStyleFlag) /// <summary> /// 스타일 제거하기 /// </summary> /// <param name="fontStyleFlag">폰트 스타일 플래그</param> public void RemoveStyle(FontStyleFlag fontStyleFlag) { this.styleToAdd &= ~((uint)fontStyleFlag); this.styleToRemove |= (uint)fontStyleFlag; } #endregion #region 추가할 스타일 포함 여부 구하기 - ContainsStyleToAdd(fontStyleFlag) /// <summary> /// 추가할 스타일 포함 여부 구하기 /// </summary> /// <param name="fontStyleFlag">폰트 스타일 플래그</param> /// <returns>추가할 스타일 포함 여부</returns> public bool ContainsStyleToAdd(FontStyleFlag fontStyleFlag) { if((this.styleToAdd & (uint)fontStyleFlag) > 0) { return true; } return false; } #endregion #region 제거할 스타일 포함 여부 구하기 - ContainsStyleToRemove(fontStyleFlag) /// <summary> /// 제거할 스타일 포함 여부 구하기 /// </summary> /// <param name="fontStyleFlag">폰트 스타일 플래그</param> /// <returns>제거할 스타일 포함 여부</returns> public bool ContainsStyleToRemove(FontStyleFlag fontStyleFlag) { if((this.styleToRemove & (uint)fontStyleFlag) > 0) { return true; } return false; } #endregion } } |
▶ Margins.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
using System; namespace TestLibrary { /// <summary> /// 마진들 /// </summary> public class Margins { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 마진 배열 /// </summary> private float[] marginArray; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 인덱서 - this[direction] /// <summary> /// 인덱서 /// </summary> /// <param name="direction">방향</param> /// <returns>마진</returns> public float this[Direction direction] { get { int i = (int)direction; if(i >= 0 && i < this.marginArray.Length) { return this.marginArray[i]; } throw new Exception("Not a valid direction."); } set { int i = (int)direction; if(i >= 0 && i < this.marginArray.Length) { this.marginArray[i] = value; } else { throw new Exception("Not a valid direction."); } } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 생성자 - Margins() /// <summary> /// 생성자 /// </summary> internal Margins() { this.marginArray = new float[4]; } #endregion #region 생성자 - Margins(top, right, bottom, left) /// <summary> /// 생성자 /// </summary> /// <param name="top">위쪽</param> /// <param name="right">오른쪽</param> /// <param name="bottom">아래쪽</param> /// <param name="left">왼쪽</param> internal Margins(float top, float right, float bottom, float left) : this() { this.marginArray[(int)Direction.Top ] = top; this.marginArray[(int)Direction.Right ] = right; this.marginArray[(int)Direction.Bottom] = bottom; this.marginArray[(int)Direction.Left ] = left; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 동일 여부 구하기 - Equals(source) /// <summary> /// 동일 여부 구하기 /// </summary> /// <param name="source">소스 마진</param> /// <returns>동일 여부</returns> public bool Equals(Margins source) { return (source.marginArray[(int) Direction.Bottom] == this.marginArray[(int) Direction.Bottom]) && (source.marginArray[(int) Direction.Left ] == this.marginArray[(int) Direction.Left ]) && (source.marginArray[(int) Direction.Right ] == this.marginArray[(int) Direction.Right ]) && (source.marginArray[(int) Direction.Top ] == this.marginArray[(int) Direction.Top ]); } #endregion } } |
▶ RTFBlock.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
namespace TestLibrary { /// <summary> /// RTF 블럭 /// </summary> /// <remarks> /// RTFBlock은 다른 블록을 포함할 수 없는 컨텐츠 블록이다. /// 예를 들어, 이미지는 다른 이미지, 단락, 테이블 등과 같은 다른 컨텐츠 블록을 포함할 수 없기 때문에 RTFBlock이다. /// </remarks> public abstract class RTFBlock : RTFRenderable { //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 수평 정렬 - HorizontalAlignment /// <summary> /// 수평 정렬 /// </summary> public abstract HorizontalAlignment HorizontalAlignment { get; set; } #endregion #region 마진들 - Margins /// <summary> /// 마진들 /// </summary> public abstract Margins Margins { get; } #endregion #region 디폴트 문자 포맷 - DefaultCharFormat /// <summary> /// 디폴트 문자 포맷 /// </summary> public abstract RTFCharFormat DefaultCharFormat { get; } #endregion #region 새 페이지 시작 여부 - StartNewPage /// <summary> /// 새 페이지 시작 여부 /// </summary> /// <remarks>true로 설정하면 이 블록은 새 페이지의 시작 부분에 정렬된다.</remarks> public abstract bool StartNewPage { get; set; } #endregion #region 블럭 헤드 - BlockHead /// <summary> /// 블럭 헤드 /// </summary> /// <remarks>이 블록에 대한 시작 RTF 제어 단어</remarks> internal abstract string BlockHead { set; } #endregion #region 블럭 테일 - BlockTail /// <summary> /// 블럭 테일 /// </summary> internal abstract string BlockTail { set; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Protected #region 정렬 코드 구하기 - GetAlignmentCode() /// <summary> /// 정렬 코드 구하기 /// </summary> /// <returns>정렬 코드</returns> protected string GetAlignmentCode() { switch(HorizontalAlignment) { case HorizontalAlignment.Left : return @"\ql"; case HorizontalAlignment.Right : return @"\qr"; case HorizontalAlignment.Center : return @"\qc"; case HorizontalAlignment.FullyJustify : return @"\qj"; default : return @"\qd"; } } #endregion } } |
▶ RTFBlockList.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
using System; using System.Collections.Generic; using System.IO; using System.Text; namespace TestLibrary { /// <summary> /// /// </summary> /// <remarks> /// 콘텐츠 블록 배열의 컨테이너이다. /// 예를 들어 각주는 단락과 이미지를 포함할 수 있기 때문에 RTFBlockList이다. /// </remarks> public class RTFBlockList : RTFRenderable { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Protected #region Field /// <summary> /// 블럭 리스트 /// </summary> protected List<RTFBlock> blockList; /// <summary> /// 디폴트 문자 포맷 /// </summary> protected RTFCharFormat defaultCharFormat; #endregion ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 문단 허용 여부 /// </summary> private bool allowParagraph; /// <summary> /// 각주 허용 여부 /// </summary> private bool allowFootnote; /// <summary> /// 제어 문자 허용 여부 /// </summary> private bool allowControlWord; /// <summary> /// 이미지 허용 여부 /// </summary> private bool allowImage; /// <summary> /// 테이블 허용 여부 /// </summary> private bool allowTable; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 디폴트 문자 포맷 - DefaultCharFormat /// <summary> /// 디폴트 문자 포맷 /// </summary> public RTFCharFormat DefaultCharFormat { get { if(this.defaultCharFormat == null) { this.defaultCharFormat = new RTFCharFormat(-1, -1, 1); } return this.defaultCharFormat; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 생성자 - RTFBlockList(allowParagraph, allowFootnote, allowControlWord, allowImage, allowTable) /// <summary> /// 생성자 /// </summary> /// <param name="allowParagraph">문단 허용 여부</param> /// <param name="allowFootnote">각주 허용 여부</param> /// <param name="allowControlWord">제어 문자 허용 여부</param> /// <param name="allowImage">이미지 허용 여부</param> /// <param name="allowTable">테이블 허용 여부</param> internal RTFBlockList(bool allowParagraph, bool allowFootnote, bool allowControlWord, bool allowImage, bool allowTable) { this.blockList = new List<RTFBlock>(); this.defaultCharFormat = null; this.allowParagraph = allowParagraph; this.allowFootnote = allowFootnote; this.allowControlWord = allowControlWord; this.allowImage = allowImage; this.allowTable = allowTable; } #endregion #region 생성자 - RTFBlockList(allowParagraph, allowTable) /// <summary> /// 생성자 /// </summary> /// <param name="allowParagraph">문단 허용 여부</param> /// <param name="allowTable">테이블 허용 여부</param> internal RTFBlockList(bool allowParagraph, bool allowTable) : this(allowParagraph, true, true, true, allowTable) { } #endregion #region 생성자 - RTFBlockList() /// <summary> /// 생성자 /// </summary> internal RTFBlockList() : this(true, true, true, true, true) { } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 문단 추가하기 - AddParagraph() /// <summary> /// 문단 추가하기 /// </summary> /// <returns>RTF 문단</returns> public RTFParagraph AddParagraph() { if(!this.allowParagraph) { throw new Exception("Paragraph is not allowed."); } RTFParagraph paragraph = new RTFParagraph(this.allowFootnote, this.allowControlWord); AddBlock(paragraph); return paragraph; } #endregion #region 섹션 추가하기 - AddSection(type, document) /// <summary> /// 섹션 추가하기 /// </summary> /// <param name="type">섹션 타입</param> /// <remarks>RTF 문서</remarks> public RTFSection AddSection(SectionType type, RTFDocument document) { RTFSection section = new RTFSection(type, document); AddBlock(section); return section; } #endregion #region 이미지 추가하기 - AddImage(filePath, imageFileType) /// <summary> /// 이미지 추가하기 /// </summary> /// <param name="filePath">파일 경로</param> /// <param name="imageFileType">이미지 파일 타입</param> /// <returns>RTF 이미지</returns> public RTFImage AddImage(string filePath, ImageFileType imageFileType) { if(!this.allowImage) { throw new Exception("Image is not allowed."); } RTFImage image = new RTFImage(filePath, imageFileType); AddBlock(image); return image; } #endregion #region 이미지 추가하기 - AddImage(filePath) /// <summary> /// 이미지 추가하기 /// </summary> /// <param name="filePath">파일 경로</param> /// <returns>RTF 이미지</returns> public RTFImage AddImage(string filePath) { int dotIndex = filePath.LastIndexOf("."); if(dotIndex < 0) { throw new Exception($"Cannot determine image type from the filename extension : {filePath}"); } string fileExtension = filePath.Substring(dotIndex + 1).ToLower(); switch(fileExtension) { case "jpg" : case "jpeg" : return AddImage(filePath, ImageFileType.JPEG); case "gif" : return AddImage(filePath, ImageFileType.GIF ); case "png" : return AddImage(filePath, ImageFileType.PNG ); default : throw new Exception($"Cannot determine image type from the filename extension : {filePath}"); } } #endregion #region 이미지 추가하기 - AddImage(memoryStream) /// <summary> /// 이미지 추가하기 /// </summary> /// <param name="memoryStream">메모리 스트림</param> /// <returns>RTF 이미지</returns> public RTFImage AddImage(MemoryStream memoryStream) { if(!this.allowImage) { throw new Exception("Image is not allowed."); } RTFImage image = new RTFImage(memoryStream); AddBlock(image); return image; } #endregion #region 테이블 추가하기 - AddTable(rowCount, columnCount, width, fontSize) /// <summary> /// 테이블 추가하기 /// </summary> /// <param name="rowCount">행 수</param> /// <param name="columnCount">열 수</param> /// <param name="width">너비</param> /// <param name="fontSize">폰트 크기</param> /// <returns>RTF 테이블</returns> public RTFTable AddTable(int rowCount, int columnCount, float width, float fontSize) { if(!this.allowTable) { throw new Exception("Table is not allowed."); } RTFTable table = new RTFTable(rowCount, columnCount, width, fontSize); AddBlock(table); return table; } #endregion #region 렌더링하기 - Render() /// <summary> /// 렌더링하기 /// </summary> /// <returns>RTF 문자열</returns> public override string Render() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(); for(int i = 0; i < this.blockList.Count; i++) { if(this.defaultCharFormat != null && this.blockList[i].DefaultCharFormat != null) { this.blockList[i].DefaultCharFormat.CopyFrom(this.defaultCharFormat); } stringBuilder.AppendLine(this.blockList[i].Render()); } return stringBuilder.ToString(); } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 블럭 전송하기 - TransferBlocksTo(targetBlockList) /// <summary> /// 블럭 전송하기 /// </summary> /// <param name="targetBlockList">타겟 블럭 리스트</param> internal void TransferBlocksTo(RTFBlockList targetBlockList) { for(int i = 0; i < this.blockList.Count; i++) { targetBlockList.AddBlock(this.blockList[i]); } this.blockList.Clear(); } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Private #region 블럭 추가하기 - AddBlock(block) /// <summary> /// 블럭 추가하기 /// </summary> /// <param name="block">블럭</param> private void AddBlock(RTFBlock block) { if(block != null) { this.blockList.Add(block); } } #endregion } } |
▶ RTFCharFormat.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 |
using System; using System.Collections.Generic; using System.Text; namespace TestLibrary { /// <summary> /// RTF 문자 포맷 /// </summary> public class RTFCharFormat { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 폰트 스타일 플래그 딕셔너리 /// </summary> private static IDictionary<FontStyleFlag, string> _fontStyleFlagDictionary = new Dictionary<FontStyleFlag, string> { { FontStyleFlag.Bold , "b" }, { FontStyleFlag.Italic , "i" }, { FontStyleFlag.Scaps , "scaps" }, { FontStyleFlag.Strike , "strike" }, { FontStyleFlag.Sub , "sub" }, { FontStyleFlag.Super , "super" }, { FontStyleFlag.Underline, "ul" } }; #endregion ////////////////////////////////////////////////////////////////////////////////////////// Instance //////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 시작 인덱스 /// </summary> private int startIndex; /// <summary> /// 종료 인덱스 /// </summary> private int endIndex; /// <summary> /// 폰트 설명자 /// </summary> private FontDescriptor fontDescriptor; /// <summary> /// ANSI 폰트 설명자 /// </summary> private FontDescriptor ansiFontDescriptor; /// <summary> /// 폰트 크기 /// </summary> private float fontSize; /// <summary> /// 폰트 스타일 /// </summary> private FontStyle fontStyle; /// <summary> /// 배경 색상 설명자 /// </summary> private ColorDescriptor backgroundColorDescriptor; /// <summary> /// 전경 색상 설명자 /// </summary> private ColorDescriptor foregroundColorDescriptor; /// <summary> /// 투인원 스타일 /// </summary> private TwoInOneStyle twoInOneStyle; /// <summary> /// 북마크 /// </summary> private string bookmark; /// <summary> /// 로컬 하이퍼링크 /// </summary> private string localHyperlink; /// <summary> /// 로컬 하이퍼링크 팁 /// </summary> private string localHyperlinkTip; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 폰트 설명자 - FontDescriptor /// <summary> /// 폰트 설명자 /// </summary> public FontDescriptor FontDescriptor { get { return this.fontDescriptor; } set { this.fontDescriptor = value; } } #endregion #region ANSI 폰트 설명자 - AnsiFontDescriptor /// <summary> /// ANSI 폰트 설명자 /// </summary> public FontDescriptor AnsiFontDescriptor { get { return this.ansiFontDescriptor; } set { this.ansiFontDescriptor = value; } } #endregion #region 폰트 크기 - FontSize /// <summary> /// 폰트 크기 /// </summary> public float FontSize { get { return this.fontSize; } set { this.fontSize = value; } } #endregion #region 폰트 스타일 - FontStyle /// <summary> /// 폰트 스타일 /// </summary> public FontStyle FontStyle { get { return this.fontStyle; } } #endregion #region 배경 색상 설명자 - BackgroundColorDescriptor /// <summary> /// 배경 색상 설명자 /// </summary> public ColorDescriptor BackgroundColorDescriptor { get { return this.backgroundColorDescriptor; } set { this.backgroundColorDescriptor = value; } } #endregion #region 전경 색상 설명자 - ForegroundColorDescriptor /// <summary> /// 전경 색상 설명자 /// </summary> public ColorDescriptor ForegroundColorDescriptor { get { return this.foregroundColorDescriptor; } set { this.foregroundColorDescriptor = value; } } #endregion #region 투인원 스타일 - TwoInOneStyle /// <summary> /// 투인원 스타일 /// </summary> public TwoInOneStyle TwoInOneStyle { get { return this.twoInOneStyle; } set { this.twoInOneStyle = value; } } #endregion #region 북마크 - Bookmark /// <summary> /// 북마크 /// </summary> public string Bookmark { get { return this.bookmark; } set { this.bookmark = value; } } #endregion #region 로컬 하이퍼링크 - LocalHyperlink /// <summary> /// 로컬 하이퍼링크 /// </summary> public string LocalHyperlink { get { return this.localHyperlink; } set { this.localHyperlink = value; } } #endregion #region 로컬 하이퍼링크 팁 - LocalHyperlinkTip /// <summary> /// 로컬 하이퍼링크 팁 /// </summary> public string LocalHyperlinkTip { get { return this.localHyperlinkTip; } set { this.localHyperlinkTip = value; } } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 시작 인덱스 - StartIndex /// <summary> /// 시작 인덱스 /// </summary> internal int StartIndex { get { return this.startIndex; } } #endregion #region 종료 인덱스 - EndIndex /// <summary> /// 종료 인덱스 /// </summary> internal int EndIndex { get { return this.endIndex; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 생성자 - RTFCharFormat(startIndex, endIndex, textLength) /// <summary> /// 생성자 /// </summary> /// <param name="startIndex">시작 인덱스</param> /// <param name="endIndex">종료 인덱스</param> /// <param name="textLength">텍스트 길이</param> internal RTFCharFormat(int startIndex, int endIndex, int textLength) { this.startIndex = -1; this.endIndex = -1; this.fontDescriptor = null; this.ansiFontDescriptor = null; this.fontSize = -1; this.fontStyle = new FontStyle(); this.backgroundColorDescriptor = null; this.foregroundColorDescriptor = null; this.twoInOneStyle = TwoInOneStyle.Disabled; this.bookmark = string.Empty; SetRange(startIndex, endIndex, textLength); } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 복사하기 - CopyFrom(format) /// <summary> /// 복사하기 /// </summary> /// <param name="format">RTF 문자 포맷</param> internal void CopyFrom(RTFCharFormat format) { if(format == null) { return; } this.startIndex = format.startIndex; this.endIndex = format.endIndex; if(this.fontDescriptor == null && format.fontDescriptor != null) { this.fontDescriptor = new FontDescriptor(format.fontDescriptor.Value); } if(this.ansiFontDescriptor == null && format.ansiFontDescriptor != null) { this.ansiFontDescriptor = new FontDescriptor(format.ansiFontDescriptor.Value); } if(this.fontSize < 0 && format.fontSize >= 0) { this.fontSize = format.fontSize; } if(this.fontStyle.IsEmpty && !format.fontStyle.IsEmpty) { this.fontStyle = new FontStyle(format.fontStyle); } if(this.backgroundColorDescriptor == null && format.backgroundColorDescriptor != null) { this.backgroundColorDescriptor = new ColorDescriptor(format.backgroundColorDescriptor.Value); } if(this.foregroundColorDescriptor == null && format.foregroundColorDescriptor != null) { this.foregroundColorDescriptor = new ColorDescriptor(format.foregroundColorDescriptor.Value); } } #endregion #region 헤더 렌더링하기 - RenderHead() /// <summary> /// 헤더 렌더링하기 /// </summary> /// <returns>헤더 RTF 문자열</returns> internal string RenderHead() { StringBuilder stringBuilder = new StringBuilder("{"); if(!string.IsNullOrEmpty(this.localHyperlink)) { stringBuilder.Append(@"{\field{\*\fldinst HYPERLINK \\l "); stringBuilder.Append("\"" + this.localHyperlink + "\""); if(!string.IsNullOrEmpty(this.localHyperlinkTip)) { stringBuilder.Append(" \\\\o \"" + this.localHyperlinkTip + "\""); } stringBuilder.Append(@"}{\fldrslt{"); } if(this.fontDescriptor != null || this.ansiFontDescriptor != null) { if(this.fontDescriptor == null) { stringBuilder.Append(@"\f" + this.ansiFontDescriptor.Value); } else if(this.ansiFontDescriptor == null) { stringBuilder.Append(@"\f" + this.fontDescriptor.Value); } else { stringBuilder.Append ( @"\loch\af" + this.ansiFontDescriptor.Value + @"\hich\af" + this.ansiFontDescriptor.Value + @"\dbch\af" + this.fontDescriptor.Value ); } } if(this.fontSize > 0) { stringBuilder.Append(@"\fs" + RTFUtility.DoubleValue(this.fontSize)); } if(this.foregroundColorDescriptor != null) { stringBuilder.Append(@"\cf" + this.foregroundColorDescriptor.Value); } if(this.backgroundColorDescriptor != null) { stringBuilder.Append ( @"\chshdng0\chcbpat" + this.backgroundColorDescriptor.Value + @"\cb" + this.backgroundColorDescriptor.Value ); } foreach(KeyValuePair<FontStyleFlag, string> keyValuePair in _fontStyleFlagDictionary) { if(FontStyle.ContainsStyleToAdd(keyValuePair.Key)) { stringBuilder.Append(@"\" + keyValuePair.Value); } else if(FontStyle.ContainsStyleToRemove(keyValuePair.Key)) { stringBuilder.Append(@"\" + keyValuePair.Value + "0"); } } if(this.twoInOneStyle != TwoInOneStyle.Disabled) { stringBuilder.Append(@"\twoinone"); switch(this.twoInOneStyle) { case TwoInOneStyle.None : stringBuilder.Append("0"); break; case TwoInOneStyle.Parentheses : stringBuilder.Append("1"); break; case TwoInOneStyle.SquareBrackets : stringBuilder.Append("2"); break; case TwoInOneStyle.AngledBrackets : stringBuilder.Append("3"); break; case TwoInOneStyle.Braces : stringBuilder.Append("4"); break; } } if(stringBuilder.ToString().Contains(@"\")) { stringBuilder.Append(" "); } if(!string.IsNullOrEmpty(this.bookmark)) { stringBuilder.Append(@"{\*\bkmkstart " + this.bookmark + "}"); } return stringBuilder.ToString(); } #endregion #region 테일 렌더링하기 - RenderTail() /// <summary> /// 테일 렌더링하기 /// </summary> /// <returns>테일 RTF 문자열</returns> internal string RenderTail() { StringBuilder stringBuilder = new StringBuilder(""); if(!string.IsNullOrEmpty(this.bookmark)) { stringBuilder.Append(@"{\*\bkmkend " + this.bookmark + "}"); } if(!string.IsNullOrEmpty(this.localHyperlink)) { stringBuilder.Append(@"}}}"); } stringBuilder.Append("}"); return stringBuilder.ToString(); } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Private #region 범위 설정하기 - SetRange(startIndex, endIndex, textLength) /// <summary> /// 범위 설정하기 /// </summary> /// <param name="startIndex">시작 인덱스</param> /// <param name="endIndex">종료 인덱스</param> /// <param name="textLength">텍스트 길이</param> private void SetRange(int startIndex, int endIndex, int textLength) { if(startIndex > endIndex) { throw new Exception($"Invalid range : ({startIndex}, {endIndex})"); } else if(startIndex < 0 || endIndex < 0) { if(startIndex != -1 || endIndex != -1) { throw new Exception($"Invalid range : ({startIndex}, {endIndex})"); } } if(endIndex >= textLength) { throw new Exception($"Range ending out of range : {endIndex}"); } this.startIndex = startIndex; this.endIndex = endIndex; } #endregion } } |
▶ RTFColor.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
using System; using System.Drawing; namespace TestLibrary { /// <summary> /// RTF 색상 /// </summary> public class RTFColor { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 색상 /// </summary> private int color; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 적색 - Red /// <summary> /// 적색 /// </summary> internal string Red { get { return ((this.color >> 16) % 256).ToString(); } } #endregion #region 녹색 - Green /// <summary> /// 녹색 /// </summary> internal string Green { get { return ((this.color >> 8) % 256).ToString(); } } #endregion #region 청색 - Blue /// <summary> /// 청색 /// </summary> internal string Blue { get { return (this.color % 256).ToString(); } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - RTFColor() /// <summary> /// 생성자 /// </summary> public RTFColor() { this.color = 0; } #endregion #region 생성자 - RTFColor(hex) /// <summary> /// 생성자 /// </summary> /// <param name="hex">16진수 색상값</param> 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); this.color = (red << 16) + (green << 8) + blue; } #endregion #region 생성자 - RTFColor(color) /// <summary> /// 생성자 /// </summary> /// <param name="color">색상</param> public RTFColor(Color color) { this.color = ( color.R << 16 ) + ( color.G << 8 ) + color.B; } #endregion #region 생성자 - RTFColor(red, green, blue) /// <summary> /// 생성자 /// </summary> /// <param name="red">적색</param> /// <param name="green">녹색</param> /// <param name="blue">청색</param> public RTFColor(byte red, byte green, byte blue) { this.color = (red << 16) + (green << 8) + blue; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 해시 코드 구하기 - GetHashCode() /// <summary> /// 해시 코드 구하기 /// </summary> /// <returns>해시 코드</returns> public override int GetHashCode() { return this.color; } #endregion #region 동일 여부 구하기 - Equals(source) /// <summary> /// 동일 여부 구하기 /// </summary> /// <param name="source">소스 객체</param> /// <returns>동일 여부</returns> public override bool Equals(object source) { RTFColor rtfColor = (RTFColor)source; return (rtfColor.color == this.color); } #endregion } } |
▶ RTFDocument.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 |
using System.Collections.Generic; using System.Drawing; using System.Text; using System.IO; namespace TestLibrary { /// <summary> /// RTF 문서 /// </summary> public class RTFDocument : RTFBlockList { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 용지 크기 /// </summary> private PaperSize paperSize; /// <summary> /// 용지 방향 /// </summary> private PaperOrientation paperOrientation; /// <summary> /// 마진들 /// </summary> private Margins margins; /// <summary> /// 지역 유형 /// </summary> private LCID lcid; /// <summary> /// 폰트 리스트 /// </summary> private List<string> fontList; /// <summary> /// 색상 리스트 /// </summary> private List<RTFColor> colorList; /// <summary> /// 헤더 /// </summary> private RTFHeaderFooter header; /// <summary> /// 바닥글 /// </summary> private RTFHeaderFooter footer; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 마진들 - Margins /// <summary> /// 마진들 /// </summary> public Margins Margins { get { return this.margins; } set { this.margins = value; } } #endregion #region 헤더 - Header /// <summary> /// 헤더 /// </summary> public RTFHeaderFooter Header { get { if(this.header == null) { this.header = new RTFHeaderFooter(HeaderFooterType.Header); } return this.header; } } #endregion #region 바닥글 - Footer /// <summary> /// 바닥글 /// </summary> public RTFHeaderFooter Footer { get { if(this.footer == null) { this.footer = new RTFHeaderFooter(HeaderFooterType.Footer); } return this.footer; } } #endregion #region 디폴트 폰트 설명자 - DefaultFontDescriptor /// <summary> /// 디폴트 폰트 설명자 /// </summary> public FontDescriptor DefaultFontDescriptor { get { return new FontDescriptor(0); } } #endregion #region 디폴트 색상 설명자 - DefaultColorDescriptor /// <summary> /// 디폴트 색상 설명자 /// </summary> public ColorDescriptor DefaultColorDescriptor { get { return new ColorDescriptor(0); } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - RTFDocument(paperSize, paperOrientation, lcid) /// <summary> /// 생성자 /// </summary> /// <param name="paperSize">용지 크기</param> /// <param name="paperOrientation">용지 방향</param> /// <param name="lcid">지역 유형</param> public RTFDocument(PaperSize paperSize, PaperOrientation paperOrientation, LCID lcid) { this.paperSize = paperSize; this.paperOrientation = paperOrientation; this.margins = new Margins(); if(this.paperOrientation == PaperOrientation.Portrait) { this.margins[Direction.Top ] = DefaultValue.SmallMargin; this.margins[Direction.Right ] = DefaultValue.LargeMargin; this.margins[Direction.Bottom] = DefaultValue.SmallMargin; this.margins[Direction.Left ] = DefaultValue.LargeMargin; } else { this.margins[Direction.Top ] = DefaultValue.LargeMargin; this.margins[Direction.Right ] = DefaultValue.SmallMargin; this.margins[Direction.Bottom] = DefaultValue.LargeMargin; this.margins[Direction.Left ] = DefaultValue.SmallMargin; } this.lcid = lcid; this.fontList = new List<string>(); this.fontList.Add(DefaultValue.Font); this.colorList = new List<RTFColor>(); this.colorList.Add(new RTFColor()); this.header = null; this.footer = null; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 디폴트 폰트 설정하기 - SetDefaultFont(fontName) /// <summary> /// 디폴트 폰트 설정하기 /// </summary> /// <param name="fontName">폰트명</param> public void SetDefaultFont(string fontName) { this.fontList[0] = fontName; } #endregion #region 폰트 설명자 생성하기 - CreateFontDescriptor(fontName) /// <summary> /// 폰트 설명자 생성하기 /// </summary> /// <param name="fontName">폰트명</param> /// <returns>폰트 설명자</returns> public FontDescriptor CreateFontDescriptor(string fontName) { if(this.fontList.Contains(fontName)) { return new FontDescriptor(this.fontList.IndexOf(fontName)); } this.fontList.Add(fontName); return new FontDescriptor(this.fontList.IndexOf(fontName)); } #endregion #region 색상 설명자 생성하기 - CreateColorDescriptor(rtfColor) /// <summary> /// 색상 설명자 생성하기 /// </summary> /// <param name="rtfColor">RTF 색상</param> /// <returns>색상 설명자</returns> public ColorDescriptor CreateColorDescriptor(RTFColor rtfColor) { if(this.colorList.Contains(rtfColor)) { return new ColorDescriptor(this.colorList.IndexOf(rtfColor)); } this.colorList.Add(rtfColor); return new ColorDescriptor(this.colorList.IndexOf(rtfColor)); } #endregion #region 색상 설명자 생성하기 - CreateColorDescriptor(color) /// <summary> /// 색상 설명자 생성하기 /// </summary> /// <param name="color">색상</param> /// <returns>색상 설명자</returns> public ColorDescriptor CreateColorDescriptor(Color color) { RTFColor rtfColor = new RTFColor(color.R, color.G, color.B); return CreateColorDescriptor(rtfColor); } #endregion #region 테이블 추가하기 - AddTable(rowCount, columnCount, fontSize) /// <summary> /// 테이블 추가하기 /// </summary> /// <param name="rowCount">행 수</param> /// <param name="columnCount">열 수</param> /// <param name="fontSize">폰트 크기</param> /// <returns>RTF 테이블</returns> public RTFTable AddTable(int rowCount, int columnCount, float fontSize) { float width; if(this.paperOrientation == PaperOrientation.Portrait) { width = RTFUtility.GetPaperWidthInPoint(this.paperSize, this.paperOrientation) - this.margins[Direction.Left] - this.margins[Direction.Right]; } else { width = RTFUtility.GetPaperHeightInPoint(this.paperSize, this.paperOrientation) - this.margins[Direction.Left] - this.margins[Direction.Right]; } return base.AddTable(rowCount, columnCount, width, fontSize); } #endregion #region 렌더링하기 - Render() /// <summary> /// 렌더링하기 /// </summary> /// <returns>RTF 문자열</returns> public override string Render() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(@"{\rtf1\ansi\deff0"); stringBuilder.AppendLine(); stringBuilder.AppendLine(@"{\fonttbl"); for(int i = 0; i < this.fontList.Count; i++) { stringBuilder.AppendLine(@"{\f" + i + " " + RTFUtility.ConvertToUnicodeEncoding(this.fontList[i].ToString()) + ";}"); } stringBuilder.AppendLine("}"); stringBuilder.AppendLine(); stringBuilder.AppendLine(@"{\colortbl"); stringBuilder.AppendLine(";"); for(int i = 1; i < this.colorList.Count; i++) { RTFColor color = this.colorList[i]; stringBuilder.AppendLine(@"\red" + color.Red + @"\green" + color.Green + @"\blue" + color.Blue + ";"); } stringBuilder.AppendLine("}"); stringBuilder.AppendLine(); stringBuilder.AppendLine ( @"\deflang" + (int)this.lcid + @"\plain\fs" + RTFUtility.DoubleValue(DefaultValue.FontSize) + @"\widowctrl\hyphauto\ftnbj" ); stringBuilder.AppendLine ( @"\paperw" + RTFUtility.GetPaperWidthInTwip(this.paperSize, this.paperOrientation) + @"\paperh" + RTFUtility.GetPaperHeightInTwip(this.paperSize, this.paperOrientation) ); stringBuilder.AppendLine(@"\margt" + RTFUtility.ConvertPointToTwip(this.margins[Direction.Top ])); stringBuilder.AppendLine(@"\margr" + RTFUtility.ConvertPointToTwip(this.margins[Direction.Right ])); stringBuilder.AppendLine(@"\margb" + RTFUtility.ConvertPointToTwip(this.margins[Direction.Bottom])); stringBuilder.AppendLine(@"\margl" + RTFUtility.ConvertPointToTwip(this.margins[Direction.Left ])); if(this.paperOrientation == PaperOrientation.Landscape) { stringBuilder.AppendLine(@"\landscape"); } if(this.header != null) { stringBuilder.Append(this.header.Render()); } if(this.footer != null) { stringBuilder.Append(this.footer.Render()); } stringBuilder.AppendLine(); stringBuilder.Append(base.Render()); stringBuilder.AppendLine("}"); return stringBuilder.ToString(); } #endregion #region 저장하기 - Save(filePath) /// <summary> /// 저장하기 /// </summary> /// <param name="filePath">파일 경로</param> public void Save(string filePath) { StreamWriter writer = new StreamWriter(filePath); writer.Write(Render()); writer.Close(); } #endregion } } |
▶ RTFFieldControlWord.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
namespace TestLibrary { /// <summary> /// RTF 필드 제어 문자 /// </summary> public class RTFFieldControlWord : RTFRenderable { //////////////////////////////////////////////////////////////////////////////////////////////////// Enumeration ////////////////////////////////////////////////////////////////////////////////////////// Public #region 필드 타입 - FieldType /// <summary> /// 필드 타입 /// </summary> public enum FieldType { /// <summary> /// 해당 무 /// </summary> None = 0, /// <summary> /// 페이지 번호 /// </summary> PageNumber, /// <summary> /// 페이지 수 /// </summary> PageCount, /// <summary> /// 날짜 /// </summary> Date, /// <summary> /// 시간 /// </summary> Time, } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 제어 문자 리스트 /// </summary> private static string[] _controlWordList = new string[] { "", @"{\field{\*\fldinst PAGE }}", @"{\field{\*\fldinst NUMPAGES }}", @"{\field{\*\fldinst DATE }}", @"{\field{\*\fldinst TIME }}" }; #endregion ////////////////////////////////////////////////////////////////////////////////////////// Instance //////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 위치 /// </summary> private int position; /// <summary> /// 필드 타입 /// </summary> private FieldType fieldType; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 위치 - Position /// <summary> /// 위치 /// </summary> internal int Position { get { return this.position; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 생성자 - RTFFieldControlWord(position, fieldType) /// <summary> /// 생성자 /// </summary> /// <param name="position">위치</param> /// <param name="fieldType">필드 타입</param> internal RTFFieldControlWord(int position, FieldType fieldType) { this.position = position; this.fieldType = fieldType; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 렌더링하기 - Render() /// <summary> /// 렌더링하기 /// </summary> /// <returns>RTF 문자열</returns> public override string Render() { return _controlWordList[(int)this.fieldType]; } #endregion } } |
▶ RTFFootnote.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
using System; using System.Text; namespace TestLibrary { /// <summary> /// RTF 각주 /// </summary> public class RTFFootnote : RTFBlockList { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 위치 /// </summary> private int position; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 위치 - Position /// <summary> /// 위치 /// </summary> internal int Position { get { return this.position; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - RTFFootnote(position, textLength) /// <summary> /// 생성자 /// </summary> /// <param name="position">위치</param> /// <param name="textLength">텍스트 길이</param> internal RTFFootnote(int position, int textLength) : base(true, false, false, true, false) { if(position < 0 || position >= textLength) { throw new Exception($"Invalid footnote position : {position} (text length={textLength})"); } this.position = position; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 렌더링하기 - Render() /// <summary> /// 렌더링하기 /// </summary> /// <returns>RTF 문자열</returns> public override string Render() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(@"{\super\chftn}"); stringBuilder.AppendLine(@"{\footnote\plain\chftn"); base.blockList[base.blockList.Count - 1].BlockTail = "}"; stringBuilder.Append(base.Render()); stringBuilder.AppendLine("}"); return stringBuilder.ToString(); } #endregion } } |
▶ RTFHeaderFooter.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
using System; using System.Text; namespace TestLibrary { /// <summary> /// RTF 헤더/바닥글 /// </summary> public class RTFHeaderFooter : RTFBlockList { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 헤더/바닥글 타입 /// </summary> private HeaderFooterType type; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 생성자 - RTFHeaderFooter(type) /// <summary> /// 생성자 /// </summary> /// <param name="type">헤더/바닥글 타입</param> internal RTFHeaderFooter(HeaderFooterType type) : base(true, false, true, true, false) { this.type = type; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 렌더링하기 - Render() /// <summary> /// 렌더링하기 /// </summary> /// <returns>RTF 문자열</returns> public override string Render() { StringBuilder stringBuilder = new StringBuilder(); if(this.type == HeaderFooterType.Header) { stringBuilder.AppendLine(@"{\header"); } else if(this.type == HeaderFooterType.Footer) { stringBuilder.AppendLine(@"{\footer"); } else { throw new Exception("Invalid HeaderFooterType"); } stringBuilder.AppendLine(); for(int i = 0; i < base.blockList.Count; i++) { if(base.defaultCharFormat != null && (base.blockList[i]).DefaultCharFormat != null) { base.blockList[i].DefaultCharFormat.CopyFrom(base.defaultCharFormat); } stringBuilder.AppendLine((blockList[i]).Render()); } stringBuilder.AppendLine("}"); return stringBuilder.ToString(); } #endregion } } |
▶ RTFImage.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 |
using System; using System.Drawing; using System.Drawing.Imaging; using System.Text; using System.IO; namespace TestLibrary { /// <summary> /// RTF 이미지 /// </summary> public class RTFImage : RTFBlock { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 이미지 파일 타입 /// </summary> private ImageFileType imageFieldType; /// <summary> /// 이미지 바이트 배열 /// </summary> private byte[] imageByteArray; /// <summary> /// 수평 정렬 /// </summary> private HorizontalAlignment horizontalAlignment; /// <summary> /// 마진들 /// </summary> private Margins margins; /// <summary> /// 너비 /// </summary> private float width; /// <summary> /// 높이 /// </summary> private float height; /// <summary> /// 종횡비 유지 여부 /// </summary> private bool keepAspectRatio; /// <summary> /// 블럭 헤드 /// </summary> private string blockHead; /// <summary> /// 블럭 테일 /// </summary> private string blockTail; /// <summary> /// 새 페이지 시작 여부 /// </summary> private bool startNewPage; /// <summary> /// 새 문단 시작 여부 /// </summary> private bool startNewParagraph; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 수평 정렬 - HorizontalAlignment /// <summary> /// 수평 정렬 /// </summary> public override HorizontalAlignment HorizontalAlignment { get { return this.horizontalAlignment; } set { this.horizontalAlignment = value; } } #endregion #region 마진들 - Margins /// <summary> /// 마진들 /// </summary> public override Margins Margins { get { return this.margins; } } #endregion #region 새 페이지 시작 여부 - StartNewPage /// <summary> /// 새 페이지 시작 여부 /// </summary> public override bool StartNewPage { get { return this.startNewPage; } set { this.startNewPage = value; } } #endregion #region 새 문단 시작 여부 - StartNewParagraph /// <summary> /// 새 문단 시작 여부 /// </summary> public bool StartNewParagraph { get { return this.startNewParagraph; } set { this.startNewParagraph = value; } } #endregion #region 너비 - Width /// <summary> /// 너비 /// </summary> public float Width { get { return this.width; } set { if(this.keepAspectRatio && this.width > 0) { float ratio = this.height / this.width; this.height = value * ratio; } this.width = value; } } #endregion #region 높이 - Height /// <summary> /// 높이 /// </summary> public float Height { get { return this.height; } set { if(this.keepAspectRatio && this.height > 0) { float ratio = this.width / this.height; this.width = value * ratio; } this.height = value; } } #endregion #region 종횡비 유지 여부 - KeepAspectRatio /// <summary> /// 종횡비 유지 여부 /// </summary> public bool KeepAspectRatio { get { return this.keepAspectRatio; } set { this.keepAspectRatio = value; } } #endregion #region 디폴트 문자 포맷 - DefaultCharFormat /// <summary> /// 디폴트 문자 포맷 /// </summary> public override RTFCharFormat DefaultCharFormat { get { return null; } } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 블럭 헤드 - BlockHead /// <summary> /// 블럭 헤드 /// </summary> internal override string BlockHead { set { this.blockHead = value; } } #endregion #region 블럭 테일 - BlockTail /// <summary> /// 블럭 테일 /// </summary> internal override string BlockTail { set { this.blockTail = value; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 생성자 - RTFImage(memoryStream) /// <summary> /// 생성자 /// </summary> /// <param name="memoryStream">메모리 스트림</param> internal RTFImage(MemoryStream memoryStream) { this.horizontalAlignment = HorizontalAlignment.Left; this.margins = new Margins(); this.keepAspectRatio = true; this.blockHead = @"{\pard"; this.blockTail = @"}"; this.startNewPage = false; this.startNewParagraph = false; this.imageByteArray = memoryStream.ToArray(); Image image = Image.FromStream(memoryStream); this.width = (image.Width / image.HorizontalResolution) * 72; this.height = (image.Height / image.VerticalResolution ) * 72; if(image.RawFormat.Equals(ImageFormat.Png)) { this.imageFieldType = ImageFileType.PNG; } else if(image.RawFormat.Equals(ImageFormat.Jpeg)) { this.imageFieldType = ImageFileType.JPEG; } else if(image.RawFormat.Equals(ImageFormat.Gif)) { this.imageFieldType = ImageFileType.GIF; } else { throw new Exception($"Image format is not supported : {image.RawFormat}"); } } #endregion #region 생성자 - RTFImage(filePath, type) /// <summary> /// 생성자 /// </summary> /// <param name="filePath">파일 경로</param> /// <param name="type">이미지 파일 타입</param> internal RTFImage(string filePath, ImageFileType type) { this.imageFieldType = type; this.horizontalAlignment = HorizontalAlignment.None; this.margins = new Margins(); this.keepAspectRatio = true; this.blockHead = @"{\pard"; this.blockTail = @"}"; this.startNewPage = false; this.startNewParagraph = false; Image image = Image.FromFile(filePath); this.width = (image.Width / image.HorizontalResolution) * 72; this.height = (image.Height / image.VerticalResolution ) * 72; using(MemoryStream memoryStream = new MemoryStream()) { image.Save(memoryStream, image.RawFormat); this.imageByteArray = memoryStream.ToArray(); } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 렌더링하기 - Render() /// <summary> /// 렌더링하기 /// </summary> /// <returns>RTF 문자열</returns> public override string Render() { StringBuilder stringBuilder = new StringBuilder(this.blockHead); if(this.startNewPage) { stringBuilder.Append(@"\pagebb"); } if(this.margins[Direction.Top] >= 0) { stringBuilder.Append(@"\sb" + RTFUtility.ConvertPointToTwip(this.margins[Direction.Top])); } if(this.margins[Direction.Bottom] >= 0) { stringBuilder.Append(@"\sa" + RTFUtility.ConvertPointToTwip(this.margins[Direction.Bottom])); } if(this.margins[Direction.Left] >= 0) { stringBuilder.Append(@"\li" + RTFUtility.ConvertPointToTwip(this.margins[Direction.Left])); } if(this.margins[Direction.Right] >= 0) { stringBuilder.Append(@"\ri" + RTFUtility.ConvertPointToTwip(this.margins[Direction.Right])); } switch(this.horizontalAlignment) { case HorizontalAlignment.Left : stringBuilder.Append(@"\ql"); break; case HorizontalAlignment.Right : stringBuilder.Append(@"\qr"); break; case HorizontalAlignment.Center : stringBuilder.Append(@"\qc"); break; } stringBuilder.AppendLine(); stringBuilder.Append(@"{\*\shppict{\pict"); if(this.imageFieldType == ImageFileType.JPEG) { stringBuilder.Append(@"\jpegblip"); } else if(this.imageFieldType == ImageFileType.PNG || this.imageFieldType == ImageFileType.GIF) { stringBuilder.Append(@"\pngblip"); } else { throw new Exception("Image type not supported."); } if(this.height > 0) { stringBuilder.Append(@"\pichgoal" + RTFUtility.ConvertPointToTwip(this.height)); } if(this.width > 0) { stringBuilder.Append(@"\picwgoal" + RTFUtility.ConvertPointToTwip(this.width)); } stringBuilder.AppendLine(); stringBuilder.AppendLine(ExtractImage()); stringBuilder.AppendLine("}}"); if(this.startNewParagraph) { stringBuilder.Append(@"\par"); } stringBuilder.AppendLine(this.blockTail); return stringBuilder.ToString(); } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Private #region 이미지 추출하기 - ExtractImage() /// <summary> /// 이미지 추출하기 /// </summary> /// <returns>이미지 RTF 문자열</returns> private string ExtractImage() { StringBuilder stringBuilder = new StringBuilder(); for(int i = 0; i < this.imageByteArray.Length; i++) { if(i != 0 && i % 60 == 0) { stringBuilder.AppendLine(); } stringBuilder.AppendFormat("{0:x2}", this.imageByteArray[i]); } return stringBuilder.ToString(); } #endregion } } |
▶ RTFParagraph.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 |
using System; using System.Collections.Generic; using System.Text; namespace TestLibrary { /// <summary> /// RTF 문단 /// </summary> public class RTFParagraph : RTFBlock { //////////////////////////////////////////////////////////////////////////////////////////////////// Class ////////////////////////////////////////////////////////////////////////////////////////// Private #region 분리 범위 - DisjointRange /// <summary> /// 분리 범위 /// </summary> private class DisjointRange { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Public #region Field /// <summary> /// 헤드 /// </summary> public int Head; /// <summary> /// 테일 /// </summary> public int Tail; /// <summary> /// 문자 포맷 /// </summary> public RTFCharFormat CharFormat; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - DisjointRange() /// <summary> /// 생성자 /// </summary> public DisjointRange() { Head = -1; Tail = -1; CharFormat = null; } #endregion } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Structure ////////////////////////////////////////////////////////////////////////////////////////// Protected #region 토큰 - Token /// <summary> /// 토큰 /// </summary> protected struct Token { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Public #region Field /// <summary> /// 텍스트 /// </summary> public string Text; /// <summary> /// 제어 여부 /// </summary> public bool IsControl; #endregion } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Protected #region Field /// <summary> /// 제어 문자 허용 여부 /// </summary> protected bool allowControlWord; /// <summary> /// 각주 허용 여부 /// </summary> protected bool allowFootnote; #endregion ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 수평 정렬 /// </summary> private HorizontalAlignment horizontalAlignment; /// <summary> /// 마진들 /// </summary> private Margins margins; /// <summary> /// 문자열 빌더 /// </summary> private StringBuilder stringBuilder; /// <summary> /// 줄 간격 /// </summary> private float lineSpacing; /// <summary> /// 블럭 헤드 /// </summary> private string blockHead; /// <summary> /// 블럭 테일 /// </summary> private string blockTail; /// <summary> /// 새 페이지 시작 여부 /// </summary> private bool startNewPage; /// <summary> /// 첫번째 줄 들여쓰기 /// </summary> private float firstLineIndent; /// <summary> /// 문자 포맷 리스트 /// </summary> private List<RTFCharFormat> charFormatList; /// <summary> /// 디폴트 문자 포맷 /// </summary> private RTFCharFormat defaultCharFormat; /// <summary> /// 필드 제어 문자 리스트 /// </summary> private List<RTFFieldControlWord> fieldControlWordList; /// <summary> /// 각주 리스트 /// </summary> private List<RTFFootnote> footnoteList; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 수평 정렬 - HorizontalAlignment /// <summary> /// 수평 정렬 /// </summary> public override HorizontalAlignment HorizontalAlignment { get { return this.horizontalAlignment; } set { this.horizontalAlignment = value; } } #endregion #region 마진들 - Margins /// <summary> /// 마진들 /// </summary> public override Margins Margins { get { return this.margins; } } #endregion #region 문자열 빌더 - StringBuilder /// <summary> /// 문자열 빌더 /// </summary> public StringBuilder StringBuilder { get { return this.stringBuilder; } } #endregion #region 줄 간격 - LineSpacing /// <summary> /// 줄 간격 /// </summary> public float LineSpacing { get { return this.lineSpacing; } set { this.lineSpacing = value; } } #endregion #region 새 페이지 시작 여부 - StartNewPage /// <summary> /// 새 페이지 시작 여부 /// </summary> public override bool StartNewPage { get { return this.startNewPage; } set { this.startNewPage = value; } } #endregion #region 첫번째 줄 들여쓰기 - FirstLineIndent /// <summary> /// 첫번째 줄 들여쓰기 /// </summary> public float FirstLineIndent { get { return this.firstLineIndent; } set { this.firstLineIndent = value; } } #endregion #region 디폴트 문자 포맷 - DefaultCharFormat /// <summary> /// 디폴트 문자 포맷 /// </summary> public override RTFCharFormat DefaultCharFormat { get { if(this.defaultCharFormat == null) { this.defaultCharFormat = new RTFCharFormat(-1, -1, this.stringBuilder.Length); } return this.defaultCharFormat; } } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 블럭 헤드 - BlockHead /// <summary> /// 블럭 헤드 /// </summary> internal override string BlockHead { set { this.blockHead = value; } } #endregion #region 블럭 테일 - BlockTail /// <summary> /// 블럭 테일 /// </summary> internal override string BlockTail { set { this.blockTail = value; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - RTFParagraph(allowFootnote, allowControlWord) /// <summary> /// 생성자 /// </summary> /// <param name="allowFootnote">각주 허용 여부</param> /// <param name="allowControlWord">제어 문자 허용 여부</param> public RTFParagraph(bool allowFootnote, bool allowControlWord) { this.stringBuilder = new StringBuilder(); this.lineSpacing = -1; this.margins = new Margins(); this.horizontalAlignment = HorizontalAlignment.Left; this.charFormatList = new List<RTFCharFormat>(); this.allowFootnote = allowFootnote; this.allowControlWord = allowControlWord; this.footnoteList = new List<RTFFootnote>(); this.fieldControlWordList = new List<RTFFieldControlWord>(); this.blockHead = @"{\pard"; this.blockTail = @"\par}"; this.startNewPage = false; this.firstLineIndent = 0; this.defaultCharFormat = null; } #endregion #region 생성자 - RTFParagraph() /// <summary> /// 생성자 /// </summary> public RTFParagraph() : this(false, false) { } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 텍스트 설정하기 - SetText(text) /// <summary> /// 텍스트 설정하기 /// </summary> /// <param name="text">텍스트</param> public void SetText(string text) { this.stringBuilder = new StringBuilder(text); } #endregion #region 문자 포맷 추가하기 - AddCharFormat(startIndex, endIndex) /// <summary> /// 문자 포맷 추가하기 /// </summary> /// <param name="startIndex">시작 인덱스</param> /// <param name="endIndex">종료 인덱스</param> /// <returns>RTF 문자 포맷</returns> public RTFCharFormat AddCharFormat(int startIndex, int endIndex) { RTFCharFormat format = new RTFCharFormat(startIndex, endIndex, this.stringBuilder.Length); this.charFormatList.Add(format); return format; } #endregion #region 문자 포맷 추가하기 - AddCharFormat() /// <summary> /// 문자 포맷 추가하기 /// </summary> /// <returns>RTF 문자 포맷</returns> public RTFCharFormat AddCharFormat() { return AddCharFormat(-1, -1); } #endregion #region 각주 추가하기 - AddFootnote(position) /// <summary> /// 각주 추가하기 /// </summary> /// <param name="position">위치</param> /// <returns>RTF 각주</returns> public RTFFootnote AddFootnote(int position) { if(!this.allowFootnote) { throw new Exception("Footnote is not allowed."); } RTFFootnote footnote = new RTFFootnote(position, this.stringBuilder.Length); this.footnoteList.Add(footnote); return footnote; } #endregion #region 제어 문자 추가하기 - AddControlWord(position, type) /// <summary> /// 제어 문자 추가하기 /// </summary> /// <param name="position">위치</param> /// <param name="type">필드 타입</param> public void AddControlWord(int position, RTFFieldControlWord.FieldType type) { if(!this.allowControlWord) { throw new Exception("ControlWord is not allowed."); } RTFFieldControlWord fieldControlWord = new RTFFieldControlWord(position, type); for(int i = 0; i < this.fieldControlWordList.Count; i++) { if(this.fieldControlWordList[i].Position == fieldControlWord.Position) { this.fieldControlWordList[i] = fieldControlWord; return; } } this.fieldControlWordList.Add(fieldControlWord); } #endregion #region 렌더링하기 - Render() /// <summary> /// 렌더링하기 /// </summary> /// <returns>RTF 문자열</returns> public override string Render() { LinkedList<Token> tokenList = BuildTokenList(); StringBuilder stringBuilder = new StringBuilder(this.blockHead); if(this.startNewPage) { stringBuilder.Append(@"\pagebb"); } if(this.lineSpacing >= 0) { stringBuilder.Append(@"\sl-" + RTFUtility.ConvertPointToTwip(this.lineSpacing) + @"\slmult0"); } if(this.margins[Direction.Top] > 0) { stringBuilder.Append(@"\sb" + RTFUtility.ConvertPointToTwip(this.margins[Direction.Top])); } if(this.margins[Direction.Bottom] > 0) { stringBuilder.Append(@"\sa" + RTFUtility.ConvertPointToTwip(this.margins[Direction.Bottom])); } if(this.margins[Direction.Left] > 0) { stringBuilder.Append(@"\li" + RTFUtility.ConvertPointToTwip(this.margins[Direction.Left])); } if(this.margins[Direction.Right] > 0) { stringBuilder.Append(@"\ri" + RTFUtility.ConvertPointToTwip(this.margins[Direction.Right])); } stringBuilder.Append(@"\fi" + RTFUtility.ConvertPointToTwip(this.firstLineIndent)); stringBuilder.Append(GetAlignmentCode()); stringBuilder.AppendLine(); if(this.defaultCharFormat != null) { stringBuilder.AppendLine(this.defaultCharFormat.RenderHead()); } stringBuilder.AppendLine(ExtractTokenList(tokenList)); if(this.defaultCharFormat != null) { stringBuilder.Append(this.defaultCharFormat.RenderTail()); } stringBuilder.AppendLine(this.blockTail); return stringBuilder.ToString(); } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Protected #region 토큰 리스트 구축하기 - BuildTokenList() /// <summary> /// 토큰 리스트 구축하기 /// </summary> /// <returns>토큰 리스트</returns> protected LinkedList<Token> BuildTokenList() { int count; Token token; LinkedList<Token> tokenList = new LinkedList<Token>(); LinkedListNode<Token> tokenNode; List<DisjointRange> disjoinRangeList = new List<DisjointRange>(); #region 나중에 사용할 수 있도록 문자 포맷 범위에서 헤드[] 및 테일[]를 빌드한다. for(int i = 0; i < this.charFormatList.Count; i++) { RTFCharFormat format = this.charFormatList[i]; DisjointRange range = null; if(format.StartIndex == -1 && format.EndIndex == -1) { range = new DisjointRange(); range.Head = 0; range.Tail = this.stringBuilder.Length - 1; range.CharFormat = format; } else if(format.StartIndex <= format.EndIndex) { range = new DisjointRange(); range.Head = format.StartIndex; range.Tail = format.EndIndex; range.CharFormat = format; } else { continue; } if(range.Tail >= this.stringBuilder.Length) { range.Tail = this.stringBuilder.Length - 1; if(range.Head > range.Tail) { continue; } } List<DisjointRange> deletList = new List<DisjointRange>(); List<DisjointRange> addList = new List<DisjointRange>(); List<DisjointRange> addAnchorList = new List<DisjointRange>(); for(int j = 0; j < disjoinRangeList.Count; j++) { DisjointRange temporaryRange = disjoinRangeList[j]; if(range.Head <= temporaryRange.Head && range.Tail >= temporaryRange.Tail) { deletList.Add(temporaryRange); } else if(range.Head <= temporaryRange.Head && range.Tail >= temporaryRange.Head && range.Tail < temporaryRange.Tail) { temporaryRange.Head = range.Tail + 1; } else if(range.Head > temporaryRange.Head && range.Head <= temporaryRange.Tail && range.Tail >= temporaryRange.Tail) { temporaryRange.Tail = range.Head - 1; } else if(range.Head > temporaryRange.Head && range.Tail < temporaryRange.Tail) { DisjointRange newRange = new DisjointRange(); newRange.Head = range.Tail + 1; newRange.Tail = temporaryRange.Tail; newRange.CharFormat = temporaryRange.CharFormat; temporaryRange.Tail = range.Head - 1; addList.Add(newRange); addAnchorList.Add(temporaryRange); } } disjoinRangeList.Add(range); for(int j = 0; j < deletList.Count; j++) { disjoinRangeList.Remove(deletList[j]); } for(int j = 0; j < addList.Count; j++) { int index = disjoinRangeList.IndexOf(addAnchorList[j]); if(index < 0) { continue; } disjoinRangeList.Insert(index, addList[j]); } } #endregion token = new Token(); token.Text = this.stringBuilder.ToString(); token.IsControl = false; tokenList.AddLast(token); #region 헤드[] 및 테일[]에서 토큰 리스트를 구축한다. for(int i = 0; i < disjoinRangeList.Count; i++) { DisjointRange range = disjoinRangeList[i]; count = 0; if(range.Head == 0) { Token newToken = new Token(); newToken.IsControl = true; newToken.Text = range.CharFormat.RenderHead(); tokenList.AddFirst(newToken); } else { tokenNode = tokenList.First; while(tokenNode != null) { Token temporaryToken = tokenNode.Value; if(!temporaryToken.IsControl) { count += temporaryToken.Text.Length; if(count == range.Head) { Token newToken = new Token(); newToken.IsControl = true; newToken.Text = range.CharFormat.RenderHead(); while(tokenNode.Next != null && tokenNode.Next.Value.IsControl) { tokenNode = tokenNode.Next; } tokenList.AddAfter(tokenNode, newToken); break; } else if(count > range.Head) { LinkedListNode<Token> newTokenNode; Token newToken1 = new Token(); newToken1.IsControl = false; newToken1.Text = temporaryToken.Text.Substring(0, temporaryToken.Text.Length - (count - range.Head)); newTokenNode = tokenList.AddAfter(tokenNode, newToken1); Token newToken2 = new Token(); newToken2.IsControl = true; newToken2.Text = range.CharFormat.RenderHead(); newTokenNode = tokenList.AddAfter(newTokenNode, newToken2); Token newToken3 = new Token(); newToken3.IsControl = false; newToken3.Text = temporaryToken.Text.Substring(temporaryToken.Text.Length - (count - range.Head)); newTokenNode = tokenList.AddAfter(newTokenNode, newToken3); tokenList.Remove(tokenNode); break; } } tokenNode = tokenNode.Next; } } count = 0; tokenNode = tokenList.First; while(tokenNode != null) { Token temporaryToken = tokenNode.Value; if(!temporaryToken.IsControl) { count += temporaryToken.Text.Length; if(count - 1 == range.Tail) { Token newToken = new Token(); newToken.IsControl = true; newToken.Text = range.CharFormat.RenderTail(); tokenList.AddAfter(tokenNode, newToken); break; } else if(count - 1 > range.Tail) { LinkedListNode<Token> newTokenNode; Token newToken1 = new Token(); newToken1.IsControl = false; newToken1.Text = temporaryToken.Text.Substring(0, temporaryToken.Text.Length - (count - range.Tail) + 1); newTokenNode = tokenList.AddAfter(tokenNode, newToken1); Token newToken2 = new Token(); newToken2.IsControl = true; newToken2.Text = range.CharFormat.RenderTail(); newTokenNode = tokenList.AddAfter(newTokenNode, newToken2); Token newToken3 = new Token(); newToken3.IsControl = false; newToken3.Text = temporaryToken.Text.Substring(temporaryToken.Text.Length - (count - range.Tail) + 1); newTokenNode = tokenList.AddAfter(newTokenNode, newToken3); tokenList.Remove(tokenNode); break; } } tokenNode = tokenNode.Next; } } #endregion #region 토큰 리스트에 각주를 삽입한다. for(int i = 0; i < this.footnoteList.Count; i++) { int position = this.footnoteList[i].Position; if(position >= this.stringBuilder.Length) { continue; } count = 0; tokenNode = tokenList.First; while(tokenNode != null) { Token temporaryToken = tokenNode.Value; if(!temporaryToken.IsControl) { count += temporaryToken.Text.Length; if(count - 1 == position) { Token newToken = new Token(); newToken.IsControl = true; newToken.Text = this.footnoteList[i].Render(); tokenList.AddAfter(tokenNode, newToken); break; } else if(count - 1 > position) { LinkedListNode<Token> newTokenNode; Token newToken1 = new Token(); newToken1.IsControl = false; newToken1.Text = temporaryToken.Text.Substring(0, temporaryToken.Text.Length - (count - position) + 1); newTokenNode = tokenList.AddAfter(tokenNode, newToken1); Token newToken2 = new Token(); newToken2.IsControl = true; newToken2.Text = this.footnoteList[i].Render(); newTokenNode = tokenList.AddAfter(newTokenNode, newToken2); Token newToken3 = new Token(); newToken3.IsControl = false; newToken3.Text = temporaryToken.Text.Substring(temporaryToken.Text.Length - (count - position) + 1); newTokenNode = tokenList.AddAfter(newTokenNode, newToken3); tokenList.Remove(tokenNode); break; } } tokenNode = tokenNode.Next; } } #endregion #region 토큰 리스트에 제어 문자를 삽입한다. for(int i = 0; i < this.fieldControlWordList.Count; i++) { int position = this.fieldControlWordList[i].Position; if(position >= this.stringBuilder.Length) { continue; } count = 0; tokenNode = tokenList.First; while(tokenNode != null) { Token temporaryToken = tokenNode.Value; if(!temporaryToken.IsControl) { count += temporaryToken.Text.Length; if(count - 1 == position) { Token newToken = new Token(); newToken.IsControl = true; newToken.Text = this.fieldControlWordList[i].Render(); tokenList.AddAfter(tokenNode, newToken); break; } else if(count - 1 > position) { LinkedListNode<Token> newTokenNode; Token newToken1 = new Token(); newToken1.IsControl = false; newToken1.Text = temporaryToken.Text.Substring(0, temporaryToken.Text.Length - (count - position) + 1); newTokenNode = tokenList.AddAfter(tokenNode, newToken1); Token newToken2 = new Token(); newToken2.IsControl = true; newToken2.Text = this.fieldControlWordList[i].Render(); newTokenNode = tokenList.AddAfter(newTokenNode, newToken2); Token newToken3 = new Token(); newToken3.IsControl = false; newToken3.Text = temporaryToken.Text.Substring(temporaryToken.Text.Length - (count - position) + 1); newTokenNode = tokenList.AddAfter(newTokenNode, newToken3); tokenList.Remove(tokenNode); break; } } tokenNode = tokenNode.Next; } } #endregion return tokenList; } #endregion #region 토큰 리스트 추출하기 - ExtractTokenList(tokenList) /// <summary> /// 토큰 리스트 추출하기 /// </summary> /// <param name="tokenList">토큰 리스트</param> /// <returns>RTF 문자열</returns> protected string ExtractTokenList(LinkedList<Token> tokenList) { LinkedListNode<Token> tokenNode; StringBuilder stringBuilder = new StringBuilder(); tokenNode = tokenList.First; while(tokenNode != null) { if(tokenNode.Value.IsControl) { stringBuilder.Append(tokenNode.Value.Text); } else { stringBuilder.Append(RTFUtility.ConvertToUnicodeEncoding(tokenNode.Value.Text)); } tokenNode = tokenNode.Next; } return stringBuilder.ToString(); } #endregion } } |
▶ RTFRenderable.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
namespace TestLibrary { /// <summary> /// RTF 렌더링 가능 객체 /// </summary> public abstract class RTFRenderable { //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 렌더링하기 - Render() /// <summary> /// 렌더링하기 /// </summary> /// <returns>RTF 문자열</returns> public abstract string Render(); #endregion } } |
▶ RTFSection.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
using System; using System.Text; namespace TestLibrary { /// <summary> /// RTF 섹션 /// </summary> public class RTFSection : RTFBlock { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 수평 정렬 /// </summary> private HorizontalAlignment horizontalAlignment; /// <summary> /// 마진들 /// </summary> private readonly Margins margins; /// <summary> /// 섹션 바닥글 /// </summary> private RTFSectionFooter sectionFooter; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 수평 정렬 - HorizontalAlignment /// <summary> /// 수평 정렬 /// </summary> public override HorizontalAlignment HorizontalAlignment { get { return this.horizontalAlignment; } set { this.horizontalAlignment = value; } } #endregion #region 마진들 - Margins /// <summary> /// 마진들 /// </summary> public override Margins Margins { get { return this.margins; } } #endregion #region 새 페이지 시작 여부 - StartNewPage /// <summary> /// 새 페이지 시작 여부 /// </summary> public override bool StartNewPage { get; set; } #endregion #region 용지 방향 - PaperOrientation /// <summary> /// 용지 방향 /// </summary> public PaperOrientation PaperOrientation { get; set; } #endregion #region 페이지 너비 - PageWidth /// <summary> /// 페이지 너비 /// </summary> public int PageWidth { get; set; } #endregion #region 페이지 높이 - PageHeight /// <summary> /// 페이지 높이 /// </summary> public int PageHeight { get; set; } #endregion #region 섹션 타입 - SectionType /// <summary> /// 섹션 타입 /// </summary> public SectionType SectionType { get; private set; } #endregion #region 섹션 바닥글 - SectionFooter /// <summary> /// 섹션 바닥글 /// </summary> public RTFSectionFooter SectionFooter { get { return this.sectionFooter ?? (this.sectionFooter = new RTFSectionFooter(this)); } } #endregion #region 디폴트 문자 포맷 - DefaultCharFormat /// <summary> /// 디폴트 문자 포맷 /// </summary> public override RTFCharFormat DefaultCharFormat { get { throw new Exception("BlockHead is not supported for sections."); } } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 블럭 헤드 - BlockHead /// <summary> /// 블럭 헤드 /// </summary> internal override string BlockHead { set { throw new Exception("BlockHead is not supported for sections."); } } #endregion #region 블럭 테일 - BlockTail /// <summary> /// 블럭 테일 /// </summary> internal override string BlockTail { set { throw new Exception("BlockHead is not supported for sections."); } } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Private #region 부모 문서 - ParentDocument /// <summary> /// 부모 문서 /// </summary> private RTFDocument ParentDocument { get; set; } #endregion #region 페이지 아래쪽에서 바닥글 위치 - FooterPositionFromPageBottom /// <summary> /// 페이지 아래쪽에서 바닥글 위치 /// </summary> private int FooterPositionFromPageBottom { get; set; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 생성자 - RTFSection(sectionType, document) /// <summary> /// 생성자 /// </summary> /// <param name="sectionType">섹션 타입</param> /// <param name="document">RTF 문서</param> internal RTFSection(SectionType sectionType, RTFDocument document) { ParentDocument = document; this.horizontalAlignment = HorizontalAlignment.None; PaperOrientation = PaperOrientation.Portrait; SectionType = sectionType; FooterPositionFromPageBottom = 720; this.sectionFooter = null; this.margins = new Margins(); } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 렌더링하기 - Render() /// <summary> /// 렌더링하기 /// </summary> /// <returns>RTF 문자열</returns> public override string Render() { StringBuilder stringBuilder = new StringBuilder(); if(SectionType == SectionType.Start) { stringBuilder.AppendLine ( string.Format ( @"{{\sectd\ltrsect\footery{0}\sectdefaultcl\sftnbj{1} ", FooterPositionFromPageBottom, GetAlignmentCode() ) ); if(PaperOrientation == PaperOrientation.Landscape) { stringBuilder.Append(@"\lndscpsxn "); } stringBuilder.Append(string.Format(@"\pgwsxn{0}\pghsxn{1} ",PageWidth, PageHeight)); if(!ParentDocument.Margins.Equals((object)Margins)) { stringBuilder.Append ( string.Format ( @"\marglsxn{0}\margrsxn{1}\margtsxn{2}\margbsxn{3} ", Margins[Direction.Left], Margins[Direction.Right], Margins[Direction.Top], Margins[Direction.Bottom] ) ); } if(SectionFooter != null) { stringBuilder.AppendLine(SectionFooter.Render()); } } else { stringBuilder.AppendLine(string.Format(@"\sect }}")); } return stringBuilder.ToString(); } #endregion } } |
▶ RTFSectionFooter.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
using System; using System.Text; namespace TestLibrary { /// <summary> /// RTF 섹션 바닥글 /// </summary> public class RTFSectionFooter : RTFBlockList { //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - RTFSectionFooter(parentSection) /// <summary> /// 생성자 /// </summary> /// <param name="parentSection">부모 섹션</param> internal RTFSectionFooter(RTFSection parentSection) : base(true, true, true, true, true) { if(parentSection == null) { throw new Exception("Section footer can only be placed within a section "); } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 렌더링하기 - Render() /// <summary> /// 렌더링하기 /// </summary> /// <returns>RTF 문자열</returns> public override string Render() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(@"{\footerr \ltrpar \pard\plain"); stringBuilder.AppendLine(@"\par "); stringBuilder.Append(base.Render()); stringBuilder.AppendLine(@"\par"); stringBuilder.AppendLine(@"}"); return stringBuilder.ToString(); } #endregion } } |
▶ RTFTable.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 |
using System; using System.Collections.Generic; using System.Text; namespace TestLibrary { /// <summary> /// RTF 테이블 /// </summary> public class RTFTable : RTFBlock { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 새 페이지 시작 여부 /// </summary> private bool startNewPage; /// <summary> /// 수평 정렬 /// </summary> private HorizontalAlignment horizontalAlignment; /// <summary> /// 마진들 /// </summary> private Margins margins; /// <summary> /// 행 수 /// </summary> private int rowCount; /// <summary> /// 열 수 /// </summary> private int columnCount; /// <summary> /// 행 높이 배열 /// </summary> private float[] rowHeightArray; /// <summary> /// 셀 패딩 배열 /// </summary> private Margins[] cellPaddingArray; /// <summary> /// 디폴트 셀 너비 /// </summary> private float defaultCellWidth; /// <summary> /// 셀 배열 /// </summary> private RTFTableCell[][] cellArray; /// <summary> /// 대표 셀 리스트 /// </summary> private List<RTFTableCell> representativeCellList; /// <summary> /// 동일 페이지 내에서 행 유지 여부 배열 /// </summary> private bool[] rowKeepInSamePageArray; /// <summary> /// 제목 행 수 /// </summary> private int titleRowCount; /// <summary> /// 폰트 크기 /// </summary> private readonly float fontSize; /// <summary> /// 디폴트 문자 포맷 /// </summary> private RTFCharFormat defaultCharFormat; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 새 페이지 시작 여부 - StartNewPage /// <summary> /// 새 페이지 시작 여부 /// </summary> public override bool StartNewPage { get { return this.startNewPage; } set { this.startNewPage = value; } } #endregion #region 수평 정렬 - HorizontalAlignment /// <summary> /// 수평 정렬 /// </summary> public override HorizontalAlignment HorizontalAlignment { get { return this.horizontalAlignment; } set { this.horizontalAlignment = value; } } #endregion #region 마진들 - Margins /// <summary> /// 마진들 /// </summary> public override Margins Margins { get { return this.margins; } } #endregion #region 행 수 - RowCount /// <summary> /// 행 수 /// </summary> public int RowCount { get { return this.rowCount; } } #endregion #region 열 수 - ColumnCount /// <summary> /// 열 수 /// </summary> public int ColumnCount { get { return this.columnCount; } } #endregion #region 헤더 배경 색상 설명자 - HeaderBackgroundColorDescriptor /// <summary> /// 헤더 배경 색상 설명자 /// </summary> public ColorDescriptor HeaderBackgroundColorDescriptor { get; set; } #endregion #region 행 배경 색상 설명자 - RowBackgroundColorDescriptor /// <summary> /// 행 배경 색상 설명자 /// </summary> public ColorDescriptor RowBackgroundColorDescriptor { get; set; } #endregion #region 행 대체 배경 색상 설명자 - RowAlternativeBackgroundColorDescriptor /// <summary> /// 행 대체 배경 색상 설명자 /// </summary> public ColorDescriptor RowAlternativeBackgroundColorDescriptor { get; set; } #endregion #region 디폴트 문자 포맷 - DefaultCharFormat /// <summary> /// 디폴트 문자 포맷 /// </summary> public override RTFCharFormat DefaultCharFormat { get { if(this.defaultCharFormat == null) { this.defaultCharFormat = new RTFCharFormat(-1, -1, 1); } return this.defaultCharFormat; } } #endregion #region 제목 행 수 - TitleRowCount /// <summary> /// 제목 행 수 /// </summary> public int TitleRowCount { get { return this.titleRowCount; } set { this.titleRowCount = value; } } #endregion #region 셀 패딩 배열 - CellPaddingArray /// <summary> /// 셀 패딩 배열 /// </summary> public Margins[] CellPaddingArray { get { return this.cellPaddingArray; } } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 블럭 헤드 - BlockHead /// <summary> /// 블럭 헤드 /// </summary> internal override string BlockHead { set { throw new Exception("BlockHead is not supported for tables."); } } #endregion #region 블럭 테일 - BlockTail /// <summary> /// 블럭 테일 /// </summary> internal override string BlockTail { set { throw new Exception("BlockHead is not supported for tables."); } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - RTFTable(rowCount, columnCount, width, fontSize) /// <summary> /// 생성자 /// </summary> /// <param name="rowCount">행 수</param> /// <param name="columnCount">열 수</param> /// <param name="width"></param> /// <param name="fontSize"></param> public RTFTable(int rowCount, int columnCount, float width, float fontSize) { this.startNewPage = false; this.horizontalAlignment = HorizontalAlignment.None; this.margins = new Margins(); this.rowCount = rowCount; this.columnCount = columnCount; this.fontSize = fontSize; this.representativeCellList = new List<RTFTableCell>(); this.titleRowCount = 0; this.cellPaddingArray = new Margins[this.rowCount]; if(this.rowCount < 1 || this.columnCount < 1) { throw new Exception("The number of rows or columns is less than 1."); } HeaderBackgroundColorDescriptor = null; RowBackgroundColorDescriptor = null; RowAlternativeBackgroundColorDescriptor = null; this.defaultCellWidth = width / (float)columnCount; this.cellArray = new RTFTableCell[this.rowCount][]; this.rowHeightArray = new float[this.rowCount]; this.rowKeepInSamePageArray = new bool[this.rowCount]; for(int i = 0; i < this.rowCount; i++) { this.cellArray[i] = new RTFTableCell[this.columnCount]; this.rowHeightArray[i] = 0f; this.rowKeepInSamePageArray[i] = false; this.cellPaddingArray[i] = new Margins(); for(int j = 0; j < this.columnCount; j++) { this.cellArray[i][j] = new RTFTableCell(this.defaultCellWidth, i, j, this); } } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 셀 구하기 - GetCell(rowIndex, columnIndex) /// <summary> /// 셀 구하기 /// </summary> /// <param name="rowIndex">행 인덱스</param> /// <param name="columnIndex">열 인덱스</param> /// <returns>RTF 테이블 셀</returns> public RTFTableCell GetCell(int rowIndex, int columnIndex) { if(this.cellArray[rowIndex][columnIndex].IsMerged) { return this.cellArray[rowIndex][columnIndex].MergeInfo.RepresentativeCell; } return this.cellArray[rowIndex][columnIndex]; } #endregion #region 행 높이 설정하기 - SetRowHeight(rowIndex, height) /// <summary> /// 행 높이 설정하기 /// </summary> /// <param name="rowIndex">행 인덱스</param> /// <param name="height">높이</param> public void SetRowHeight(int rowIndex, float height) { if(rowIndex < 0 || rowIndex >= this.rowCount) { throw new Exception("Row index out of range"); } for(int i = 0; i < this.columnCount; i++) { if(this.cellArray[rowIndex][i].IsMerged && this.cellArray[rowIndex][i].MergeInfo.RepresentativeCell.MergeInfo.RowSpan > 1) { throw new Exception("Row height cannot be set because some cell in this row has been merged."); } } this.rowHeightArray[rowIndex] = height; } #endregion #region 열 너비 설정하기 - SetColumnWidth(columnIndex, width) /// <summary> /// 열 너비 설정하기 /// </summary> /// <param name="columnIndex">열 인덱스</param> /// <param name="width">너비</param> public void SetColumnWidth(int columnIndex, float width) { if(columnIndex < 0 || columnIndex >= this.columnCount) { throw new Exception("Column index out of range"); } for(int i = 0; i < this.rowCount; i++) { if(this.cellArray[i][columnIndex].IsMerged) { throw new Exception("Column width cannot be set because some cell in this column has been merged."); } } for(int i = 0; i < this.rowCount; i++) { this.cellArray[i][columnIndex].Width = width; } } #endregion #region 동일 페이지에서 행 유지 여부 설정하기 - SetRowKeepInSamePage(rowIndex, allow) /// <summary> /// 동일 페이지에서 행 유지 여부 설정하기 /// </summary> /// <param name="rowIndex">행 인덱스</param> /// <param name="allow">허용 여부</param> public void SetRowKeepInSamePage(int rowIndex, bool allow) { if(rowIndex < 0 || rowIndex >= this.rowCount) { throw new Exception("Row index out of range"); } this.rowKeepInSamePageArray[rowIndex] = allow; } #endregion #region 병합하기 - Merge(topRowIndex, leftColumnIndex, rowSpan, columnSpan) /// <summary> /// 병합하기 /// </summary> /// <param name="topRowIndex">위쪽 행 인덱스</param> /// <param name="leftColumnIndex">왼쪽 열 인덱스</param> /// <param name="rowSpan">행 범위</param> /// <param name="columnSpan">열 범위</param> /// <returns>RTF 테이블 셀</returns> public RTFTableCell Merge(int topRowIndex, int leftColumnIndex, int rowSpan, int columnSpan) { if(topRowIndex < 0 || topRowIndex >= this.rowCount) { throw new Exception("Row index out of range"); } if(leftColumnIndex < 0 || leftColumnIndex >= this.columnCount) { throw new Exception("Column index out of range"); } if(rowSpan < 1 || topRowIndex + rowSpan - 1 >= this.rowCount) { throw new Exception("Row span out of range."); } if(columnSpan < 1 || leftColumnIndex + columnSpan - 1 >= this.columnCount) { throw new Exception("Column span out of range."); } if(columnSpan == 1 && rowSpan == 1) { return GetCell(topRowIndex, leftColumnIndex); } for(int i = 0; i < rowSpan; i++) { for(int j = 0; j < columnSpan; j++) { if(this.cellArray[topRowIndex + i][leftColumnIndex + j].IsMerged) { throw new Exception("Cannot merge cells because some of the cells has been merged."); } } } float width = 0; for(int i = 0; i < rowSpan; i++) { for(int j = 0; j < columnSpan; j++) { if(i == 0) { width += this.cellArray[topRowIndex][leftColumnIndex + j].Width; } this.cellArray[topRowIndex + i][leftColumnIndex + j].MergeInfo = new CellMergeInfo ( i, rowSpan, j, columnSpan, this.cellArray[topRowIndex][leftColumnIndex] ); if(i != 0 || j != 0) { this.cellArray[topRowIndex + i][leftColumnIndex + j].TransferBlocksTo ( this.cellArray[topRowIndex + i][leftColumnIndex + j].MergeInfo.RepresentativeCell ); } } } this.cellArray[topRowIndex][leftColumnIndex].Width = width; this.representativeCellList.Add(this.cellArray[topRowIndex][leftColumnIndex]); return this.cellArray[topRowIndex][leftColumnIndex]; } #endregion #region 내부 테두리 설정하기 - SetInnerBorder(style, width, colorDescriptor) /// <summary> /// 내부 테두리 설정하기 /// </summary> /// <param name="style">테두리 스타일</param> /// <param name="width">너비</param> /// <param name="colorDescriptor">색상 설명자</param> public void SetInnerBorder(BorderStyle style, float width, ColorDescriptor colorDescriptor) { for(int i = 0; i < this.rowCount; i++) { for(int j = 0; j < this.columnCount; j++) { if(i == 0) { this.cellArray[i][j].Borders[Direction.Bottom].Width = width; this.cellArray[i][j].Borders[Direction.Bottom].Style = style; this.cellArray[i][j].Borders[Direction.Bottom].ColorDescriptor = colorDescriptor; } else if(i == this.rowCount - 1) { this.cellArray[i][j].Borders[Direction.Top].Width = width; this.cellArray[i][j].Borders[Direction.Top].Style = style; this.cellArray[i][j].Borders[Direction.Top].ColorDescriptor = colorDescriptor; } else { this.cellArray[i][j].Borders[Direction.Top ].Width = width; this.cellArray[i][j].Borders[Direction.Top ].Style = style; this.cellArray[i][j].Borders[Direction.Top ].ColorDescriptor = colorDescriptor; this.cellArray[i][j].Borders[Direction.Bottom].Width = width; this.cellArray[i][j].Borders[Direction.Bottom].Style = style; this.cellArray[i][j].Borders[Direction.Bottom].ColorDescriptor = colorDescriptor; } if(j == 0) { this.cellArray[i][j].Borders[Direction.Right].Width = width; this.cellArray[i][j].Borders[Direction.Right].Style = style; this.cellArray[i][j].Borders[Direction.Right].ColorDescriptor = colorDescriptor; } else if(j == this.columnCount - 1) { this.cellArray[i][j].Borders[Direction.Left].Width = width; this.cellArray[i][j].Borders[Direction.Left].Style = style; this.cellArray[i][j].Borders[Direction.Left].ColorDescriptor = colorDescriptor; } else { this.cellArray[i][j].Borders[Direction.Right].Width = width; this.cellArray[i][j].Borders[Direction.Right].Style = style; this.cellArray[i][j].Borders[Direction.Right].ColorDescriptor = colorDescriptor; this.cellArray[i][j].Borders[Direction.Left].Width = width; this.cellArray[i][j].Borders[Direction.Left].Style = style; this.cellArray[i][j].Borders[Direction.Left].ColorDescriptor = colorDescriptor; } } } } #endregion #region 내부 테두리 설정하기 - SetInnerBorder(style, width) /// <summary> /// 내부 테두리 설정하기 /// </summary> /// <param name="style">테두리 스타일</param> /// <param name="width">너비</param> public void SetInnerBorder(BorderStyle style, float width) { SetInnerBorder(style, width, new ColorDescriptor(0)); } #endregion #region 외부 테두리 설정하기 - SetOuterBorder(style, width, colorDescriptor) /// <summary> /// 외부 테두리 설정하기 /// </summary> /// <param name="style">테두리 스타일</param> /// <param name="width">너비</param> /// <param name="colorDescriptor">색상 설명자</param> public void SetOuterBorder(BorderStyle style, float width, ColorDescriptor colorDescriptor) { for(int i = 0; i < this.columnCount; i++) { this.cellArray[0][i].Borders[Direction.Top].Width = width; this.cellArray[0][i].Borders[Direction.Top].Style = style; this.cellArray[0][i].Borders[Direction.Top].ColorDescriptor = colorDescriptor; this.cellArray[this.rowCount - 1][i].Borders[Direction.Bottom].Width = width; this.cellArray[this.rowCount - 1][i].Borders[Direction.Bottom].Style = style; this.cellArray[this.rowCount - 1][i].Borders[Direction.Bottom].ColorDescriptor = colorDescriptor; } for(int i = 0; i < this.rowCount; i++) { this.cellArray[i][0].Borders[Direction.Left].Width = width; this.cellArray[i][0].Borders[Direction.Left].Style = style; this.cellArray[i][0].Borders[Direction.Left].ColorDescriptor = colorDescriptor; this.cellArray[i][this.columnCount - 1].Borders[Direction.Right].Width = width; this.cellArray[i][this.columnCount - 1].Borders[Direction.Right].Style = style; this.cellArray[i][this.columnCount - 1].Borders[Direction.Right].ColorDescriptor = colorDescriptor; } } #endregion #region 외부 테두리 설정하기 - SetOuterBorder(style, width) /// <summary> /// 외부 테두리 설정하기 /// </summary> /// <param name="style">테두리 스타일</param> /// <param name="width">너비</param> public void SetOuterBorder(BorderStyle style, float width) { SetOuterBorder(style, width, new ColorDescriptor(0)); } #endregion #region 헤더 테두리 색상 설정하기 - SetHeaderBorderColors(outerColorDescriptor, innerColorDescriptor) /// <summary> /// 헤더 테두리 색상 설정하기 /// </summary> /// <param name="outerColorDescriptor">외부 색상 설명자</param> /// <param name="innerColorDescriptor">내부 색상 설명자</param> public void SetHeaderBorderColors(ColorDescriptor outerColorDescriptor, ColorDescriptor innerColorDescriptor) { for(int j = 0; j < this.columnCount; j++) { this.cellArray[0][j].Borders[Direction.Top ].ColorDescriptor = outerColorDescriptor; this.cellArray[0][j].Borders[Direction.Bottom].ColorDescriptor = innerColorDescriptor; if(j == 0) { this.cellArray[0][j].Borders[Direction.Right].ColorDescriptor = innerColorDescriptor; this.cellArray[0][j].Borders[Direction.Left ].ColorDescriptor = outerColorDescriptor; } else if(j == this.columnCount - 1) { this.cellArray[0][j].Borders[Direction.Right].ColorDescriptor = outerColorDescriptor; this.cellArray[0][j].Borders[Direction.Left ].ColorDescriptor = innerColorDescriptor; } else { this.cellArray[0][j].Borders[Direction.Right].ColorDescriptor = innerColorDescriptor; this.cellArray[0][j].Borders[Direction.Left ].ColorDescriptor = innerColorDescriptor; } } } #endregion #region 렌더링하기 - Render() /// <summary> /// 렌더링하기 /// </summary> /// <returns>RTF 문자열</returns> public override string Render() { StringBuilder stringBuilder = new StringBuilder(); ValidateAllMergedCellBorders(); if(this.defaultCharFormat != null) { for(int i = 0; i < this.rowCount; i++) { for(int j = 0; j < this.columnCount; j++) { if(this.cellArray[i][j].IsMerged && this.cellArray[i][j].MergeInfo.RepresentativeCell != this.cellArray[i][j]) { continue; } if(this.cellArray[i][j].DefaultCharFormat != null) { this.cellArray[i][j].DefaultCharFormat.CopyFrom(this.defaultCharFormat); } } } } float topMargin = this.margins[Direction.Top] - this.fontSize; if(this.startNewPage || topMargin > 0) { stringBuilder.Append(@"{\pard"); if(this.startNewPage) { stringBuilder.Append(@"\pagebb"); } if(this.margins[Direction.Top] >= 0) { stringBuilder.Append(@"\sl-" + RTFUtility.ConvertPointToTwip(topMargin)); } else { stringBuilder.Append(@"\sl-1"); } stringBuilder.AppendLine(@"\slmult0\par}"); } int columnAccumulator; for(int i = 0; i < this.rowCount; i++) { columnAccumulator = 0; stringBuilder.Append ( @"{\trowd\trgaph" + string.Format ( @"\trpaddl{0}\trpaddt{1}\trpaddr{2}\trpaddb{3}", RTFUtility.ConvertPointToTwip(CellPaddingArray[i][Direction.Left ]), RTFUtility.ConvertPointToTwip(CellPaddingArray[i][Direction.Top ]), RTFUtility.ConvertPointToTwip(CellPaddingArray[i][Direction.Right ]), RTFUtility.ConvertPointToTwip(CellPaddingArray[i][Direction.Bottom]) ) ); switch(this.horizontalAlignment) { case HorizontalAlignment.Left : stringBuilder.Append(@"\trql"); break; case HorizontalAlignment.Right : stringBuilder.Append(@"\trqr"); break; case HorizontalAlignment.Center : stringBuilder.Append(@"\trqc"); break; case HorizontalAlignment.FullyJustify : stringBuilder.Append(@"\trqj"); break; } stringBuilder.AppendLine(); if(this.margins[Direction.Left] >= 0) { stringBuilder.AppendLine(@"\trleft" + RTFUtility.ConvertPointToTwip(this.margins[Direction.Left])); columnAccumulator = RTFUtility.ConvertPointToTwip(this.margins[Direction.Left]); } if(this.rowHeightArray[i] > 0) { stringBuilder.Append(@"\trrh" + RTFUtility.ConvertPointToTwip(this.rowHeightArray[i])); } if(this.rowKeepInSamePageArray[i]) { stringBuilder.Append(@"\trkeep"); } if(i < this.titleRowCount) { stringBuilder.Append(@"\trhdr"); } stringBuilder.AppendLine(); for(int j = 0; j < this.columnCount; j++) { if(this.cellArray[i][j].IsMerged && !this.cellArray[i][j].IsBeginOfColumnSpan) { continue; } float nextCellLeftBorderClearance = j < this.columnCount - 1 ? GetCell(i, j + 1).OuterLeftBorderClearance : 0; columnAccumulator += RTFUtility.ConvertPointToTwip(GetCell(i, j).Width); int columnRightPosition = columnAccumulator; if(nextCellLeftBorderClearance < 0) { columnRightPosition += RTFUtility.ConvertPointToTwip(nextCellLeftBorderClearance); columnRightPosition = columnRightPosition == 0 ? 1 : columnRightPosition; } for(Direction direction = Direction.Top; direction <= Direction.Left; direction++) { Border border = GetCell(i, j).Borders[direction]; if(border.Style != BorderStyle.None) { stringBuilder.Append(@"\clbrdr"); switch(direction) { case Direction.Top : stringBuilder.Append("t"); break; case Direction.Right : stringBuilder.Append("r"); break; case Direction.Bottom : stringBuilder.Append("b"); break; case Direction.Left : stringBuilder.Append("l"); break; } stringBuilder.Append(@"\brdrw" + RTFUtility.ConvertPointToTwip(border.Width)); stringBuilder.Append(@"\brdr"); switch(border.Style) { case BorderStyle.Single : stringBuilder.Append("s" ); break; case BorderStyle.Dotted : stringBuilder.Append("dot" ); break; case BorderStyle.Dashed : stringBuilder.Append("dash"); break; case BorderStyle.Double : stringBuilder.Append("db" ); break; default : throw new Exception("Unkown border style"); } stringBuilder.Append(@"\brdrcf" + border.ColorDescriptor.Value); } } if(GetCell(i, j).BackgroundColorDescriptor != null) { stringBuilder.Append(string.Format(@"\clcbpat{0}", GetCell(i, j).BackgroundColorDescriptor.Value)); } else if(i == 0 && HeaderBackgroundColorDescriptor != null) { stringBuilder.Append(string.Format(@"\clcbpat{0}", HeaderBackgroundColorDescriptor.Value)); } else if(RowBackgroundColorDescriptor != null && (RowAlternativeBackgroundColorDescriptor == null || i % 2 == 0)) { stringBuilder.Append(string.Format(@"\clcbpat{0}", RowBackgroundColorDescriptor.Value)); } else if(RowBackgroundColorDescriptor != null && RowAlternativeBackgroundColorDescriptor != null && i % 2 != 0) { stringBuilder.Append(string.Format(@"\clcbpat{0}", RowAlternativeBackgroundColorDescriptor.Value)); } if(this.cellArray[i][j].IsMerged && this.cellArray[i][j].MergeInfo.RowSpan > 1) { if(this.cellArray[i][j].IsBeginOfRowSpan) { stringBuilder.Append(@"\clvmgf"); } else { stringBuilder.Append(@"\clvmrg"); } } switch(this.cellArray[i][j].VerticalAlignment) { case VerticalAlignment.Top : stringBuilder.Append(@"\clvertalt"); break; case VerticalAlignment.Middle : stringBuilder.Append(@"\clvertalc"); break; case VerticalAlignment.Bottom : stringBuilder.Append(@"\clvertalb"); break; } stringBuilder.AppendLine(@"\cellx" + columnRightPosition); } for(int j = 0; j < this.columnCount; j++) { if(!this.cellArray[i][j].IsMerged || this.cellArray[i][j].IsBeginOfColumnSpan) { stringBuilder.Append(this.cellArray[i][j].Render()); } } stringBuilder.AppendLine(@"\row}"); } if(this.margins[Direction.Bottom] >= 0) { stringBuilder.Append(@"\sl-" + RTFUtility.ConvertPointToTwip(this.margins[Direction.Bottom]) + @"\slmult"); } return stringBuilder.ToString(); } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Private #region 병합 셀 테두리 검증하기 - ValidateMergedCellBorder(representativeCell, direction) /// <summary> /// 병합 셀 테두리 검증하기 /// </summary> /// <param name="representativeCell">대표 셀</param> /// <param name="direction">방향</param> private void ValidateMergedCellBorder(RTFTableCell representativeCell, Direction direction) { if(!representativeCell.IsMerged) { throw new Exception("Invalid representative (cell is not merged)."); } Dictionary<Border, int> borderDictionary = new Dictionary<Border, int>(); Border majorityBorder; int majorityCount; int limit = (direction == Direction.Top || direction == Direction.Bottom) ? representativeCell.MergeInfo.ColumnSpan : representativeCell.MergeInfo.RowSpan; for(int i = 0; i < limit; i++) { int rowIndex; int columnIndex; Border border; if(direction == Direction.Top || direction == Direction.Bottom) { if(direction == Direction.Top) { rowIndex = 0; } else { rowIndex = representativeCell.MergeInfo.RowSpan - 1; } columnIndex = i; } else { if(direction == Direction.Right) { columnIndex = representativeCell.MergeInfo.ColumnSpan - 1; } else { columnIndex = 0; } rowIndex = i; } border = this.cellArray[representativeCell.RowIndex + rowIndex][representativeCell.ColumnIndex + columnIndex].Borders[direction]; if(borderDictionary.ContainsKey(border)) { borderDictionary[border] = (int)borderDictionary[border] + 1; } else { borderDictionary[border] = 1; } } majorityCount = -1; majorityBorder = representativeCell.Borders[direction]; foreach(KeyValuePair<Border, int> keyValuePair in borderDictionary) { if(keyValuePair.Value > majorityCount) { majorityCount = keyValuePair.Value; majorityBorder.Width = keyValuePair.Key.Width; majorityBorder.Style = keyValuePair.Key.Style; majorityBorder.ColorDescriptor = keyValuePair.Key.ColorDescriptor; } } } #endregion #region 모든 병합 셀 테두리들 검증하기 - ValidateAllMergedCellBorders(representativeCell) /// <summary> /// 모든 병합 셀 테두리들 검증하기 /// </summary> /// <param name="representativeCell">대표 셀</param> private void ValidateMergedCellBorders(RTFTableCell representativeCell) { if(!representativeCell.IsMerged) { throw new Exception("Invalid representative (cell is not merged)."); } ValidateMergedCellBorder(representativeCell, Direction.Top ); ValidateMergedCellBorder(representativeCell, Direction.Right ); ValidateMergedCellBorder(representativeCell, Direction.Bottom); ValidateMergedCellBorder(representativeCell, Direction.Left ); } #endregion #region 모든 병합 셀 테두리들 검증하기 - ValidateAllMergedCellBorders() /// <summary> /// 모든 병합 셀 테두리들 검증하기 /// </summary> private void ValidateAllMergedCellBorders() { for(int i = 0; i < this.representativeCellList.Count; i++) { ValidateMergedCellBorders(this.representativeCellList[i]); } } #endregion } } |
▶ RTFTableCell.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 |
using System.Text; namespace TestLibrary { /// <summary> /// RTF 테이블 셀 /// </summary> public class RTFTableCell : RTFBlockList { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 수평 정렬 /// </summary> private HorizontalAlignment horizontalAlignment; /// <summary> /// 수직 정렬 /// </summary> private VerticalAlignment verticalAlignment; /// <summary> /// 너비 /// </summary> private float width; /// <summary> /// 테두리들 /// </summary> private Borders borders; /// <summary> /// 병합 정보 /// </summary> private CellMergeInfo mergeInfo; /// <summary> /// 행 인덱스 /// </summary> private int rowIndex; /// <summary> /// 열 인덱스 /// </summary> private int columnIndex; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 행 인덱스 - RowIndex /// <summary> /// 행 인덱스 /// </summary> public int RowIndex { get { return this.rowIndex; } } #endregion #region 열 인덱스 - ColumnIndex /// <summary> /// 열 인덱스 /// </summary> public int ColumnIndex { get { return this.columnIndex; } } #endregion #region 너비 - Width /// <summary> /// 너비 /// </summary> public float Width { get { return this.width; } set { this.width = value; } } #endregion #region 병합 여부 - IsMerged /// <summary> /// 병합 여부 /// </summary> public bool IsMerged { get { if(this.mergeInfo == null) { return false; } return true; } } #endregion #region 테두리들 - Borders /// <summary> /// 테두리들 /// </summary> public Borders Borders { get { return this.borders; } } #endregion #region 외부 왼쪽 테두리 정리 - OuterLeftBorderClearance /// <summary> /// 외부 왼쪽 테두리 정리 /// </summary> public float OuterLeftBorderClearance { get; set; } #endregion #region 수평 정렬 - HorizontalAlignment /// <summary> /// 수평 정렬 /// </summary> public HorizontalAlignment HorizontalAlignment { get { return this.horizontalAlignment; } set { this.horizontalAlignment = value; } } #endregion #region 수직 정렬 - VerticalAlignment /// <summary> /// 수직 정렬 /// </summary> public VerticalAlignment VerticalAlignment { get { return this.verticalAlignment; } set { this.verticalAlignment = value; } } #endregion #region 부모 테이블 - ParentTable /// <summary> /// 부모 테이블 /// </summary> public RTFTable ParentTable { get; private set; } #endregion #region 배경 색상 설명자 - BackgroundColorDescriptor /// <summary> /// 배경 색상 설명자 /// </summary> public ColorDescriptor BackgroundColorDescriptor { get; set; } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 병합 정보 - MergeInfo /// <summary> /// 병합 정보 /// </summary> internal CellMergeInfo MergeInfo { get { return this.mergeInfo; } set { this.mergeInfo = value; } } #endregion #region 행 범위 시작 여부 - IsBeginOfRowSpan /// <summary> /// 행 범위 시작 여부 /// </summary> internal bool IsBeginOfRowSpan { get { if(this.mergeInfo == null) { return false; } return (this.mergeInfo.RowIndex == 0); } } #endregion #region 열 범위 시작 여부 - IsBeginOfColumnSpan /// <summary> /// 컬열 범위 시작 여부 /// </summary> internal bool IsBeginOfColumnSpan { get { if(this.mergeInfo == null) { return false; } return (this.mergeInfo.ColumnIndex == 0); } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Internal #region 생성자 - RTFTableCell(width, rowIndex, columnIndex, parentTable) /// <summary> /// 생성자 /// </summary> /// <param name="width">너비</param> /// <param name="rowIndex">행 인덱스</param> /// <param name="columnIndex">열 인덱스</param> /// <param name="parentTable">부모 테이블</param> internal RTFTableCell(float width, int rowIndex, int columnIndex, RTFTable parentTable) : base(true, false) { this.horizontalAlignment = HorizontalAlignment.None; this.verticalAlignment = VerticalAlignment.Top; this.width = width; this.borders = new Borders(); this.mergeInfo = null; this.rowIndex = rowIndex; this.columnIndex = columnIndex; BackgroundColorDescriptor = null; ParentTable = parentTable; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 테두리 색상 설정하기 - SetBorderColor(colorDescriptor) /// <summary> /// 테두리 색상 설정하기 /// </summary> /// <param name="colorDescriptor">색상 설명자</param> public void SetBorderColor(ColorDescriptor colorDescriptor) { this.Borders[Direction.Top ].ColorDescriptor = colorDescriptor; this.Borders[Direction.Bottom].ColorDescriptor = colorDescriptor; this.Borders[Direction.Left ].ColorDescriptor = colorDescriptor; this.Borders[Direction.Right ].ColorDescriptor = colorDescriptor; } #endregion #region 렌더링하기 - Render() /// <summary> /// 렌더링하기 /// </summary> /// <returns>RTF 문자열</returns> public override string Render() { StringBuilder stringBuilder = new StringBuilder(); string alignment = string.Empty; switch(this.horizontalAlignment) { case HorizontalAlignment.Left : alignment = @"\ql"; break; case HorizontalAlignment.Right : alignment = @"\qr"; break; case HorizontalAlignment.Center : alignment = @"\qc"; break; case HorizontalAlignment.FullyJustify : alignment = @"\qj"; break; case HorizontalAlignment.Distributed : alignment = @"\qd"; break; } if(base.blockList.Count <= 0) { stringBuilder.AppendLine(@"\pard\intbl"); } else { for(int i = 0; i < base.blockList.Count; i++) { RTFBlock block = (RTFBlock) base.blockList[i]; if(defaultCharFormat != null && block.DefaultCharFormat != null) { block.DefaultCharFormat.CopyFrom(defaultCharFormat); } if(block.Margins[Direction.Top] < 0) { block.Margins[Direction.Top] = 0; } if(block.Margins[Direction.Right] < 0) { block.Margins[Direction.Right] = 0; } if(block.Margins[Direction.Bottom] < 0) { block.Margins[Direction.Bottom] = 0; } if(block.Margins[Direction.Left] < 0) { block.Margins[Direction.Left] = 0; } if(i == 0) { block.BlockHead = @"\pard\intbl" + alignment; } else { block.BlockHead = @"\par" + alignment; } block.BlockTail = string.Empty; stringBuilder.AppendLine(block.Render()); } } stringBuilder.AppendLine(@"\cell"); return stringBuilder.ToString(); } #endregion } } |
▶ RTFUtility.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
using System; using System.Text; namespace TestLibrary { /// <summary> /// RTF 유틸리티 /// </summary> public static class RTFUtility { //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Public #region 밀리미터→포인트 변환하기 - ConvertMilimeterToPoint(mm) /// <summary> /// 밀리미터→포인트 변환하기 /// </summary> /// <param name="mm">밀리미터 값</param> /// <returns>포인트 값</returns> public static float ConvertMilimeterToPoint(float mm) { return mm * (float) 2.836; } #endregion #region 밀리미터→트윕 변환하기 - ConvertMilimeterToTwip(mm) /// <summary> /// 밀리미터→트윕 변환하기 /// </summary> /// <param name="mm">밀리미터 값</param> /// <returns>트윕 값</returns> public static int ConvertMilimeterToTwip(float mm) { double inches = mm * 0.0393700787; return Convert.ToInt32(inches * 1440); } #endregion #region 포인트→트윕 변환하기 - ConvertPointToTwip(points) /// <summary> /// 포인트→트윕 변환하기 /// </summary> /// <param name="points">포인트 값</param> /// <returns>트윕 값</returns> public static int ConvertPointToTwip(float points) { return !float.IsNaN(points) ? Convert.ToInt32(points * 20) : 0; } #endregion #region 2배 만들기 - DoubleValue(points) /// <summary> /// 2배 만들기 /// </summary> /// <param name="points">포인트 값</param> /// <returns>2배 포인트 값</returns> public static int DoubleValue(float points) { return Convert.ToInt32(points * 2); } #endregion #region 용지 너비 구하기 (단위 : 트윕) - GetPaperWidthInTwip(paperSize, paperOrientation) /// <summary> /// 용지 너비 구하기 (단위 : 트윕) /// </summary> /// <param name="paperSize">용지 크기</param> /// <param name="paperOrientation">용지 방향</param> /// <returns>용지 너비 (단위 : 트윕)</returns> public static int GetPaperWidthInTwip(PaperSize paperSize, PaperOrientation paperOrientation) { int[] dimensionArray = GetPaperDimensionArray(paperSize); if(paperOrientation == PaperOrientation.Portrait) { if(dimensionArray[0] < dimensionArray[1]) { return dimensionArray[0]; } else { return dimensionArray[1]; } } else { if(dimensionArray[0] < dimensionArray[1]) { return dimensionArray[1]; } else { return dimensionArray[0]; } } } #endregion #region 용지 높이 구하기 (단위 : 트윕) - GetPaperHeightInTwip(paperSize, paperOrientation) /// <summary> /// 용지 높이 구하기 (단위 : 트윕) /// </summary> /// <param name="paperSize">용지 크기</param> /// <param name="paperOrientation">용지 방향</param> /// <returns>용지 높이 (단위 : 트윕)</returns> public static int GetPaperHeightInTwip(PaperSize paperSize, PaperOrientation paperOrientation) { int[] dimensionArray = GetPaperDimensionArray(paperSize); if(paperOrientation == PaperOrientation.Portrait) { if(dimensionArray[0] < dimensionArray[1]) { return dimensionArray[1]; } else { return dimensionArray[0]; } } else { if(dimensionArray[0] < dimensionArray[1]) { return dimensionArray[0]; } else { return dimensionArray[1]; } } } #endregion #region 용지 너비 구하기 (단위 : 포인트) - GetPaperWidthInPoint(paperSize, paperOrientation) /// <summary> /// 용지 너비 구하기 (단위 : 포인트) /// </summary> /// <param name="paperSize">용지 크기</param> /// <param name="paperOrientation">용지 방향</param> /// <returns>용지 너비 (단위 : 포인트)</returns> public static float GetPaperWidthInPoint(PaperSize paperSize, PaperOrientation paperOrientation) { return (float) GetPaperWidthInTwip(paperSize, paperOrientation) / 20.0f; } #endregion #region 용지 높이 구하기 (단위 : 포인트) - GetPaperHeightInPoint(paperSize, paperOrientation) /// <summary> /// 용지 높이 구하기 (단위 : 포인트) /// </summary> /// <param name="paperSize">용지 크기</param> /// <param name="paperOrientation">용지 방향</param> /// <returns>용지 높이 (단위 : 포인트)</returns> public static float GetPaperHeightInPoint(PaperSize paperSize, PaperOrientation paperOrientation) { return (float)GetPaperHeightInTwip(paperSize, paperOrientation) / 20.0f; } #endregion #region 유니코드 인코딩으로 변환하기 - ConvertToUnicodeEncoding(source) /// <summary> /// 유니코드 인코딩으로 변환하기 /// </summary> /// <param name="source">소스 문자열</param> /// <returns>유니코딩 인코딩 변환 문자열</returns> public static string ConvertToUnicodeEncoding(string source) { StringBuilder stringBuilder = new StringBuilder(); int unicode; for(int i = 0; i < source.Length; i++) { unicode = (int)source[i]; if(source[i] == '\n') { stringBuilder.AppendLine(@"\line"); } else if(source[i] == '\r') { } else if(source[i] == '\t') { stringBuilder.Append(@"\tab "); } else if(unicode <= 0xff) { if(unicode == 0x5c || unicode == 0x7b || unicode == 0x7d) { stringBuilder.Append(@"\'" + string.Format("{0:x2}", unicode)); } else if(0x00 <= unicode && unicode < 0x20) { stringBuilder.Append(@"\'" + string.Format("{0:x2}", unicode)); } else if(0x20 <= unicode && unicode < 0x80) { stringBuilder.Append(source[i]); } else { stringBuilder.Append(@"\'" + string.Format("{0:x2}", unicode)); } } else if(0xff < unicode && unicode <= 0x8000) { stringBuilder.Append(@"\uc1\u" + unicode + "*"); } else if(0x8000 < unicode && unicode <= 0xffff) { stringBuilder.Append(@"\uc1\u" + (unicode - 0x10000) + "*"); } else { stringBuilder.Append(@"\uc1\u9633*"); } } return stringBuilder.ToString(); } #endregion #region 빅5 인코딩으로 변환하기 - ConvertToBig5Encoding(source) /// <summary> /// 빅5 인코딩으로 변환하기 /// </summary> /// <param name="source">소스 문자열</param> /// <returns>빅5 인코딩 변환 문자열</returns> public static string ConvertToBig5Encoding(string source) { string result = string.Empty; Encoding big5Encoding = Encoding.GetEncoding(950); Encoding asciiEncoding = Encoding.ASCII; byte[] byteArray = big5Encoding.GetBytes(source); byte character; for(int i = 0; i < byteArray.Length; i++) { character = byteArray[i]; if ( (0x00 <= character && character < 0x20) || (0x80 <= character && character <= 0xff) || character == 0x5c || character == 0x7b || character == 0x7d ) { result += string.Format(@"\'{0:x2}", character); } else { result += asciiEncoding.GetChars(new byte[] { character })[0]; } } return result; } #endregion //////////////////////////////////////////////////////////////////////////////// Private #region 용지 차원 배열 구하기 - GetPaperDimensionArray(paperSize) /// <summary> /// 용지 차원 배열 구하기 /// </summary> /// <param name="paperSize">용지 크기</param> /// <returns>용지 차원 배열</returns> private static int[] GetPaperDimensionArray(PaperSize paperSize) { switch(paperSize) { case PaperSize.Letter : return new int[] { 15840, 12240 }; case PaperSize.A3 : return new int[] { 16838, 23811 }; case PaperSize.A4 : return new int[] { 11906, 16838 }; default : throw new Exception("Unknow paper size."); } } #endregion } } |
[TestProject 프로젝트]
▶ Program.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 |
using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using TestLibrary; namespace TestPrjoect { /// <summary> /// 프로그램 /// </summary> internal class Program { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region Field private static RTFDocument _document = null; /// <summary> /// 폰트명 딕셔너리 /// </summary> private static Dictionary<string, FontDescriptor> _fontNameDictionary = new Dictionary<string, FontDescriptor>(); /// <summary> /// 색상 딕셔너리 /// </summary> private static Dictionary<Color, ColorDescriptor> _colorDictionary = new Dictionary<Color, ColorDescriptor>(); #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region 폰트 설명자 구하기 - GetFontDescriptor(fontName) /// <summary> /// 폰트 설명자 구하기 /// </summary> /// <param name="fontName">폰트명</param> /// <returns>폰트 설명자</returns> private static FontDescriptor GetFontDescriptor(string fontName) { if(_fontNameDictionary.ContainsKey(fontName)) { return _fontNameDictionary[fontName]; } else { FontDescriptor descriptor = _document.CreateFontDescriptor(fontName); _fontNameDictionary.Add(fontName, descriptor); return descriptor; } } #endregion #region 색상 설명자 구하기 - GetColorDescriptor(color) /// <summary> /// 색상 설명자 구하기 /// </summary> /// <param name="color">색상</param> /// <returns>색상 설명자</returns> private static ColorDescriptor GetColorDescriptor(Color color) { if(_colorDictionary.ContainsKey(color)) { return _colorDictionary[color]; } else { ColorDescriptor descriptor = _document.CreateColorDescriptor(color); _colorDictionary.Add(color, descriptor); return descriptor; } } #endregion #region 문단 설정하기 - SetParagraph(paragraph, alignment, fontName, fontSize, textColor, text) /// <summary> /// 문단 설정하기 /// </summary> /// <param name="paragraph">RTF 문단</param> /// <param name="alignment">수평 정렬</param> /// <param name="fontName">폰트명</param> /// <param name="fontSize">폰트 크기</param> /// <param name="textColor">텍스트 색상</param> /// <param name="text">텍스트</param> private static void SetParagraph(RTFParagraph paragraph, HorizontalAlignment? alignment, string fontName, float? fontSize, Color? textColor, string text) { if(alignment.HasValue) { paragraph.HorizontalAlignment = alignment.Value; } if(fontName != null) { paragraph.DefaultCharFormat.FontDescriptor = GetFontDescriptor(fontName); } if(fontSize.HasValue) { paragraph.DefaultCharFormat.FontSize = fontSize.Value; } if(textColor.HasValue) { paragraph.DefaultCharFormat.ForegroundColorDescriptor = GetColorDescriptor(textColor.Value); } if(text != null) { paragraph.SetText(text); } } #endregion #region 헤더 문단 추가하기 - AddHeaderParagraph(alignment, fontName, fontSize, textColor, text) /// <summary> /// 헤더 문단 추가하기 /// </summary> /// <param name="alignment">수평 정렬</param> /// <param name="fontName">폰트명</param> /// <param name="fontSize">폰트 크기</param> /// <param name="textColor">텍스트 색상</param> /// <param name="text">텍스트</param> /// <returns>RTF 문단</returns> private static RTFParagraph AddHeaderParagraph(HorizontalAlignment? alignment, string fontName, float? fontSize, Color? textColor, string text) { RTFParagraph paragraph = _document.Header.AddParagraph(); SetParagraph(paragraph, alignment, fontName, fontSize, textColor, text); return paragraph; } #endregion #region 바닥글 문단 추가하기 - AddFooterParagraph(alignment, fontName, fontSize, textColor, text) /// <summary> /// 바닥글 문단 추가하기 /// </summary> /// <param name="alignment">수평 정렬</param> /// <param name="fontName">폰트명</param> /// <param name="fontSize">폰트 크기</param> /// <param name="textColor">텍스트 색상</param> /// <param name="text">텍스트</param> /// <returns>RTF 문단</returns> private static RTFParagraph AddFooterParagraph(HorizontalAlignment? alignment, string fontName, float? fontSize, Color? textColor, string text) { RTFParagraph paragraph = _document.Footer.AddParagraph(); SetParagraph(paragraph, alignment, fontName, fontSize, textColor, text); return paragraph; } #endregion #region 빈 문단 추가하기 - AddEmptyParagraph() /// <summary> /// 빈 문단 추가하기 /// </summary> /// <returns>RTF 문단</returns> private static RTFParagraph AddEmptyParagraph() { RTFParagraph paragraph = _document.AddParagraph(); return paragraph; } #endregion #region 문단 추가하기 - AddParagraph(alignment, fontName, fontSize, textColor, text) /// <summary> /// 문단 추가하기 /// </summary> /// <param name="alignment">수평 정렬</param> /// <param name="fontName">폰트명</param> /// <param name="fontSize">폰트 크기</param> /// <param name="textColor">텍스트 색상</param> /// <param name="text">텍스트</param> /// <returns>RTF 문단</returns> private static RTFParagraph AddParagraph(HorizontalAlignment? alignment, string fontName, float? fontSize, Color? textColor, string text) { RTFParagraph paragraph = _document.AddParagraph(); SetParagraph(paragraph, alignment,fontName, fontSize, textColor, text); return paragraph; } #endregion #region 문자 포맷 추가하기 - AddCharFormat(paragraph, startIndex, endIndex, fontName, fontSize, backgroundColor, foregroundColor) /// <summary> /// 문자 포맷 추가하기 /// </summary> /// <param name="paragraph">RTF 문단</param> /// <param name="startIndex">시작 인덱스</param> /// <param name="endIndex">종료 인덱스</param> /// <param name="fontName">폰트명</param> /// <param name="fontSize">폰트 크기</param> /// <param name="backgroundColor">배경 색상</param> /// <param name="foregroundColor">전경 색상</param> /// <returns>RTF 문자 포맷</returns> private static RTFCharFormat AddCharFormat ( RTFParagraph paragraph, int startIndex, int endIndex, string fontName, float? fontSize, Color? backgroundColor, Color? foregroundColor ) { RTFCharFormat format = paragraph.AddCharFormat(startIndex, endIndex); if(fontName != null) { format.FontDescriptor = GetFontDescriptor(fontName); } if(fontSize.HasValue) { format.FontSize = fontSize.Value; } if(backgroundColor.HasValue) { format.BackgroundColorDescriptor = GetColorDescriptor(backgroundColor.Value); } if(foregroundColor.HasValue) { format.ForegroundColorDescriptor = GetColorDescriptor(foregroundColor.Value); } return format; } #endregion #region 스타일 추가하기 - AddStyle(format, fontStyleFlag) /// <summary> /// 스타일 추가하기 /// </summary> /// <param name="format">RTF 문자 포맷</param> /// <param name="fontStyleFlag">폰트 스타일 플래그</param> private static void AddStyle(RTFCharFormat format, FontStyleFlag fontStyleFlag) { format.FontStyle.AddStyle(fontStyleFlag); } #endregion #region 각주 추가하기 - AddFootnote(paragraph, footnoteIndex) /// <summary> /// 각주 추가하기 /// </summary> /// <param name="paragraph">RTF 문단</param> /// <param name="footnoteIndex">각주 인덱스</param> /// <returns>RTF 각주</returns> private static RTFFootnote AddFootnote(RTFParagraph paragraph, int footnoteIndex) { RTFFootnote footnote = paragraph.AddFootnote(footnoteIndex); return footnote; } #endregion #region 각주 문단 추가하기 - AddFootnoteParagraph(footnote, fontName, fontSize, textColor, text) /// <summary> /// 각주 문단 추가하기 /// </summary> /// <param name="footnote">RTF 각주</param> /// <param name="fontName">폰트명</param> /// <param name="fontSize">폰트 크기</param> /// <param name="textColor">텍스트 색상</param> /// <param name="text">텍스트</param> /// <returns>RTF 문단</returns> private static RTFParagraph AddFootnoteParagraph(RTFFootnote footnote, string fontName, float? fontSize, Color? textColor, string text) { RTFParagraph paragraph = footnote.AddParagraph(); SetParagraph(paragraph, null, fontName, fontSize, textColor, text); return paragraph; } #endregion #region 제어 문자 추가하기 - AddControlWord(paragraph, index, .fieldType) /// <summary> /// 제어 문자 추가하기 /// </summary> /// <param name="paragraph">RTF 문단</param> /// <param name="index">인덱스</param> /// <param name="fieldType">필드 타입</param> private static void AddControlWord(RTFParagraph paragraph, int index, RTFFieldControlWord.FieldType fieldType) { paragraph.AddControlWord(index, fieldType); } #endregion #region 이미지 추가하기 - AddImage(filePath, imageFileType, startNewParagraph, alignment, width, height) /// <summary> /// 이미지 추가하기 /// </summary> /// <param name="filePath">파일 경로</param> /// <param name="imageFileType">이미지 파일 타입</param> /// <param name="startNewParagraph">새 문단 시작 여부</param> /// <param name="alignment">수평 정렬</param> /// <param name="width">너비</param> /// <param name="height">높이</param> /// <returns>RTF 이미지</returns> private static RTFImage AddImage(string filePath, ImageFileType imageFileType, bool? startNewParagraph, HorizontalAlignment? alignment, float? width, float? height) { RTFImage image = _document.AddImage(filePath, imageFileType); if(startNewParagraph.HasValue) { image.StartNewParagraph = startNewParagraph.Value; } if(alignment.HasValue) { image.HorizontalAlignment = alignment.Value; } if(width.HasValue) { image.Width = width.Value; } if(height.HasValue) { image.Height = height.Value; } return image; } #endregion #region 프로그램 시작하기 - Main() /// <summary> /// 프로그램 시작하기 /// </summary> private static void Main() { #region 문서를 설정한다. _document = new RTFDocument(PaperSize.A4, PaperOrientation.Landscape, LCID.Korean); _document.DefaultCharFormat.FontDescriptor = GetFontDescriptor("나눔고딕코딩"); _document.DefaultCharFormat.FontSize = 12f; _document.DefaultCharFormat.ForegroundColorDescriptor = GetColorDescriptor(Color.Black); #endregion #region 헤더를 추가한다. RTFParagraph paragraph1 = AddHeaderParagraph(HorizontalAlignment.Center, null, null, Color.DarkGray, "문서 헤더"); #endregion #region 바닥글을 추가한다. RTFParagraph paragraph2 = AddFooterParagraph(HorizontalAlignment.Center, null, null, Color.DarkGray, "페이지 : / , 날짜 : , 시간 : "); AddControlWord(paragraph2, 5 , RTFFieldControlWord.FieldType.PageNumber); AddControlWord(paragraph2, 8 , RTFFieldControlWord.FieldType.PageCount ); AddControlWord(paragraph2, 15, RTFFieldControlWord.FieldType.Date ); AddControlWord(paragraph2, 22, RTFFieldControlWord.FieldType.Time ); #endregion #region 문단을 추가한다. AddEmptyParagraph(); AddParagraph(HorizontalAlignment.Left , null, null, null, "일반 문단 입니다."); AddParagraph(HorizontalAlignment.Center, null, null, null, "일반 문단 입니다."); AddParagraph(HorizontalAlignment.Right , null, null, null, "일반 문단 입니다."); #endregion #region 문단에서 특정 문자열의 포맷을 설정한다. AddEmptyParagraph(); RTFParagraph paragraph3 = AddParagraph(HorizontalAlignment.Left, null, null, null, "123456789"); RTFParagraph paragraph4 = AddParagraph(HorizontalAlignment.Left, null, null, null, "123456789"); RTFParagraph paragraph5 = AddParagraph(HorizontalAlignment.Left, null, null, null, "123456789"); RTFCharFormat format1 = AddCharFormat(paragraph3, 0, 2, null, null, Color.Red , Color.White); RTFCharFormat format2 = AddCharFormat(paragraph4, 3, 5, null, null, Color.Green, Color.White); RTFCharFormat format3 = AddCharFormat(paragraph5, 6, 8, null, null, Color.Blue , Color.White); AddStyle(format1, FontStyleFlag.Italic ); AddStyle(format2, FontStyleFlag.Bold ); AddStyle(format3, FontStyleFlag.Underline); #endregion #region 각주를 추가한다. AddEmptyParagraph(); RTFParagraph paragraph6 = AddParagraph(null, null, null, null, "각주 테스트 문자열 입니다."); RTFFootnote footnote1 = AddFootnote(paragraph6, 14); AddFootnoteParagraph(footnote1, "굴림체", 10f, Color.Red, "각주 테스트 문자열 입니다."); RTFParagraph paragraph7 = AddParagraph(null, null, null, null, "각주 테스트 문자열 입니다."); RTFFootnote footnote2 = AddFootnote(paragraph7, 14); AddFootnoteParagraph(footnote2, "굴림체", 10f, Color.Blue, "각주 테스트 문자열 입니다."); #endregion #region 이미지를 추가한다. AddEmptyParagraph(); AddImage("IMAGE\\sample.png", ImageFileType.PNG, true, null, null, 100); #endregion #region 테이블을 추가한다. AddEmptyParagraph(); RTFTable table = _document.AddTable(4, 4, 600, 12); table.SetInnerBorder(BorderStyle.Dotted, 1f); table.SetOuterBorder(BorderStyle.Single, 2f); table.HeaderBackgroundColorDescriptor = GetColorDescriptor(Color.DarkGreen); table.RowBackgroundColorDescriptor = GetColorDescriptor(Color.White); for(int y = 0; y < table.RowCount; y++) { table.SetRowHeight(y, 25f); for(int x = 0; x < table.ColumnCount; x++) { RTFTableCell cell1 = table.GetCell(y, x); RTFParagraph paragraph8 = cell1.AddParagraph(); SetParagraph(paragraph8, HorizontalAlignment.Center, null, null, null, $"({y},{x})"); cell1.VerticalAlignment = VerticalAlignment.Middle; cell1.DefaultCharFormat.FontSize = 16f; } } table.Merge(1, 0, 3, 1); RTFTableCell cell2 = table.GetCell(3, 3); cell2.BackgroundColorDescriptor = GetColorDescriptor(Color.Red); #endregion #region 투인원 스타일을 설정한다. AddEmptyParagraph(); RTFParagraph paragraph9 = AddParagraph(null, null, null, Color.DarkGray, "가나다라마바사아자차카타파하"); RTFCharFormat format4 = AddCharFormat(paragraph9, 5, 10, null, null, null, null); format4.TwoInOneStyle = TwoInOneStyle.SquareBrackets; #endregion #region 하이퍼링크를 추가한다. AddEmptyParagraph(); RTFParagraph paragraph10 = AddParagraph(null, null, null, null, "하이퍼링크 문자열을 추가합니다."); RTFCharFormat format5 = AddCharFormat(paragraph10, 0, 4, null, null, null, Color.Blue); format5.LocalHyperlink = "target"; format5.LocalHyperlinkTip = "타겟으로 연결합니다."; #endregion #region 새로운 페이지를 시작한다. RTFParagraph paragraph11 = AddParagraph(null, null, null, null, "새로운 페이지를 시작합니다."); paragraph11.StartNewPage = true; #endregion #region 북마크를 설정한다. RTFParagraph paragraph12 = AddParagraph(null, null, null, null, "target 북마크를 설정합니다."); RTFCharFormat format6 = AddCharFormat(paragraph12, 0, 17, null, null, null, null); format6.Bookmark = "target"; #endregion #region RTF 파일을 저장한다. _document.Save("demo.rtf"); #endregion #region MS WORD를 실행한다. Process process = new Process {StartInfo = {FileName = "demo.rtf"}}; process.Start(); #endregion } #endregion } } |