#region MigraDoc - Creating Documents on the Fly
//
// Authors:
// Stefan Lange
// Klaus Potzesny
// David Stephensen
//
// Copyright (c) 2001-2017 empira Software GmbH, Cologne Area (Germany)
//
// http://www.pdfsharp.com
// http://www.migradoc.com
// http://sourceforge.net/projects/pdfsharp
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using MigraDoc.DocumentObjectModel.publics;
using MigraDoc.DocumentObjectModel.Visitors;
namespace MigraDoc.DocumentObjectModel
{
///
/// Represents the collection of all styles.
///
public class Styles : DocumentObjectCollection, IVisitable
{
///
/// Initializes a new instance of the Styles class.
///
public Styles()
{
SetupStyles();
}
///
/// Initializes a new instance of the Styles class with the specified parent.
///
public Styles(DocumentObject parent)
: base(parent)
{
SetupStyles();
}
#region Methods
///
/// Creates a deep copy of this object.
///
public new Styles Clone()
{
return (Styles)base.DeepCopy();
}
///
/// Gets a style by its name.
///
public Style this[string styleName]
{
get
{
int count = Count;
// index starts from 1; DefaultParagraphFont cannot be modified.
for (int index = 1; index < count; ++index)
{
Style style = this[index];
if (String.Compare(style.Name, styleName, StringComparison.OrdinalIgnoreCase) == 0)
return style;
}
return null;
}
}
///
/// Gets a style by index.
///
public new Style this[int index]
{
get { return (Style)base[index]; }
}
///
/// Gets the index of a style by name.
///
/// Name of the style looking for.
/// Index or -1 if it does not exist.
public int GetIndex(string styleName)
{
if (styleName == null)
throw new ArgumentNullException("styleName");
int count = Count;
for (int index = 0; index < count; ++index)
{
Style style = this[index];
if (String.Compare(style.Name, styleName, StringComparison.OrdinalIgnoreCase) == 0)
return index;
}
return -1;
}
///
/// Adds a new style to the styles collection.
///
/// Name of the style.
/// Name of the base style.
public Style AddStyle(string name, string baseStyleName)
{
if (name == null || baseStyleName == null)
throw new ArgumentNullException(name == null ? "name" : "baseStyleName");
if (name == "" || baseStyleName == "")
throw new ArgumentException(name == "" ? "name" : "baseStyleName");
Style style = new Style();
style._name.Value = name;
style._baseStyle.Value = baseStyleName;
Add(style);
// Add(style) may add a clone of style, therefore we return the style by name.
return this[name];
}
///
/// Adds a DocumentObject to the styles collection. Will sometimes add a clone of the DocumentObject, not the object passed as parameter.
///
public override void Add(DocumentObject value)
{
if (value == null)
throw new ArgumentNullException("value");
Style style = value as Style;
if (style == null)
throw new InvalidOperationException(DomSR.StyleExpected);
bool isRootStyle = style.IsRootStyle;
if (style.BaseStyle == "" && !isRootStyle)
throw new ArgumentException(DomSR.UndefinedBaseStyle(style.BaseStyle));
Style baseStyle = null;
int styleIndex = GetIndex(style.BaseStyle);
if (styleIndex != -1)
baseStyle = this[styleIndex] as Style;
else if (!isRootStyle)
throw new ArgumentException(DomSR.UndefinedBaseStyle(style.BaseStyle));
if (baseStyle != null)
style._styleType.Value = (int)baseStyle.Type;
int index = GetIndex(style.Name);
if (index >= 0)
{
// Here a clone of the object will be added to the list, not the original object.
style = style.Clone();
style._parent = this;
((IList)this)[index] = style;
}
else
base.Add(value);
}
#endregion
#region Properties
///
/// Gets the default paragraph style.
///
public Style Normal
{
get { return this[Style.DefaultParagraphName]; }
}
///
/// Gets or sets a comment associated with this object.
///
public string Comment
{
get { return _comment.Value; }
set { _comment.Value = value; }
}
[DV]
public NString _comment = NString.NullValue;
#endregion
///
/// Initialize the built-in styles.
///
public void SetupStyles()
{
Style style;
// First standard style.
style = new Style(Style.DefaultParagraphFontName, null);
style.IsReadOnly = true;
style._styleType.Value = (int)StyleType.Character;
style._buildIn.Value = true;
Add(style);
// Normal 'Standard' (Paragraph Style).
style = new Style(Style.DefaultParagraphName, null);
style._styleType.Value = (int)StyleType.Paragraph;
style._buildIn.Value = true;
style.Font.Name = "Arial"; // Not "Verdana" anymore.
style.Font.Size = 10;
style.Font.Bold = false;
style.Font.Italic = false;
style.Font.Underline = Underline.None;
style.Font.Color = Colors.Black;
style.Font.Subscript = false;
style.Font.Superscript = false;
style.ParagraphFormat.Alignment = ParagraphAlignment.Left;
style.ParagraphFormat.FirstLineIndent = 0;
style.ParagraphFormat.LeftIndent = 0;
style.ParagraphFormat.RightIndent = 0;
style.ParagraphFormat.KeepTogether = false;
style.ParagraphFormat.KeepWithNext = false;
style.ParagraphFormat.SpaceBefore = 0;
style.ParagraphFormat.SpaceAfter = 0;
style.ParagraphFormat.LineSpacing = 10;
style.ParagraphFormat.LineSpacingRule = LineSpacingRule.Single;
style.ParagraphFormat.OutlineLevel = OutlineLevel.BodyText;
style.ParagraphFormat.PageBreakBefore = false;
style.ParagraphFormat.WidowControl = true;
Add(style);
// Heading1 'Überschrift 1' (Paragraph Style).
style = new Style(StyleNames.Heading1, StyleNames.Normal);
style._buildIn.Value = true;
style.ParagraphFormat.OutlineLevel = OutlineLevel.Level1;
Add(style);
// Heading2 'Überschrift 2' (Paragraph Style).
style = new Style(StyleNames.Heading2, StyleNames.Heading1);
style._buildIn.Value = true;
style.ParagraphFormat.OutlineLevel = OutlineLevel.Level2;
Add(style);
// Heading3 'Überschrift 3' (Paragraph Style).
style = new Style(StyleNames.Heading3, StyleNames.Heading2);
style._buildIn.Value = true;
style.ParagraphFormat.OutlineLevel = OutlineLevel.Level3;
Add(style);
// Heading4 'Überschrift 4' (Paragraph Style).
style = new Style(StyleNames.Heading4, StyleNames.Heading3);
style._buildIn.Value = true;
style.ParagraphFormat.OutlineLevel = OutlineLevel.Level4;
Add(style);
// Heading5 'Überschrift 5' (Paragraph Style).
style = new Style(StyleNames.Heading5, StyleNames.Heading4);
style._buildIn.Value = true;
style.ParagraphFormat.OutlineLevel = OutlineLevel.Level5;
Add(style);
// Heading6 'Überschrift 6' (Paragraph Style).
style = new Style(StyleNames.Heading6, StyleNames.Heading5);
style._buildIn.Value = true;
style.ParagraphFormat.OutlineLevel = OutlineLevel.Level6;
Add(style);
// Heading7 'Überschrift 7' (Paragraph Style).
style = new Style(StyleNames.Heading7, StyleNames.Heading6);
style._buildIn.Value = true;
style.ParagraphFormat.OutlineLevel = OutlineLevel.Level7;
Add(style);
// Heading8 'Überschrift 8' (Paragraph Style).
style = new Style(StyleNames.Heading8, StyleNames.Heading7);
style._buildIn.Value = true;
style.ParagraphFormat.OutlineLevel = OutlineLevel.Level8;
Add(style);
// Heading9 'Überschrift 9' (Paragraph Style).
style = new Style(StyleNames.Heading9, StyleNames.Heading8);
style._buildIn.Value = true;
style.ParagraphFormat.OutlineLevel = OutlineLevel.Level9;
Add(style);
// List 'Liste' (Paragraph Style).
style = new Style(StyleNames.List, StyleNames.Normal);
style._buildIn.Value = true;
Add(style);
// Footnote 'Fußnote' (Paragraph Style).
style = new Style(StyleNames.Footnote, StyleNames.Normal);
style._buildIn.Value = true;
Add(style);
// Header 'Kopfzeile' (Paragraph Style).
style = new Style(StyleNames.Header, StyleNames.Normal);
style._buildIn.Value = true;
Add(style);
// -33: Footer 'Fußzeile' (Paragraph Style).
style = new Style(StyleNames.Footer, StyleNames.Normal);
style._buildIn.Value = true;
Add(style);
// Hyperlink 'Hyperlink' (Character Style).
style = new Style(StyleNames.Hyperlink, StyleNames.DefaultParagraphFont);
style._buildIn.Value = true;
Add(style);
// InvalidStyleName 'Ungültiger Formatvorlagenname' (Paragraph Style).
style = new Style(StyleNames.InvalidStyleName, StyleNames.Normal);
style._buildIn.Value = true;
style.Font.Bold = true;
style.Font.Underline = Underline.Dash;
style.Font.Color = new Color(0xFF00FF00);
Add(style);
}
#region public
///
/// Converts Styles into DDL.
///
public override void Serialize(Serializer serializer)
{
serializer.WriteComment(_comment.Value);
int pos = serializer.BeginContent("\\styles");
// A style can only be added to Styles if its base style exists. Therefore the
// styles collection is consistent at any one time by definition. But because it
// is possible to change the base style of a style, the sequence of the styles
// in the styles collection can be in an order that a style comes before its base
// style. The styles in an DDL file must be ordered such that each style appears
// after its base style. We cannot simply reorder the styles collection, because
// the predefined styles are expected at a fixed position.
// The solution is to reorder the styles during serialization.
int count = Count;
bool[] fSerialized = new bool[count]; // already serialized
fSerialized[0] = true; // consider DefaultParagraphFont as serialized
bool[] fSerializePending = new bool[count]; // currently serializing
bool newLine = false; // gets true if at least one style was written
//Start from 1 and do not serialize DefaultParagraphFont
for (int index = 1; index < count; index++)
{
if (!fSerialized[index])
{
Style style = this[index];
SerializeStyle(serializer, index, ref fSerialized, ref fSerializePending, ref newLine);
}
}
serializer.EndContent(pos);
}
///
/// Serialize a style, but serialize its base style first (if that was not yet done).
///
void SerializeStyle(Serializer serializer, int index, ref bool[] fSerialized, ref bool[] fSerializePending,
ref bool newLine)
{
Style style = this[index];
// It is not possible to modify the default paragraph font
if (style.Name == Style.DefaultParagraphFontName)
return;
// Circular dependencies cannot occur if changing the base style is implemented
// correctly. But before we proof that, we check it here.
if (fSerializePending[index])
{
string message = String.Format("Circular dependency detected according to style '{0}'.", style.Name);
throw new InvalidOperationException(message);
}
// Only style 'Normal' has no base style
if (style.BaseStyle != "")
{
int idxBaseStyle = GetIndex(style.BaseStyle);
if (idxBaseStyle != -1)
{
if (!fSerialized[idxBaseStyle])
{
fSerializePending[index] = true;
SerializeStyle(serializer, idxBaseStyle, ref fSerialized, ref fSerializePending, ref newLine);
fSerializePending[index] = false;
}
}
}
int pos2 = serializer.BeginBlock();
if (newLine)
serializer.WriteLineNoCommit();
style.Serialize(serializer);
if (serializer.EndBlock(pos2))
newLine = true;
fSerialized[index] = true;
}
///
/// Allows the visitor object to visit the document object and its child objects.
///
void IVisitable.AcceptVisitor(DocumentObjectVisitor visitor, bool visitChildren)
{
visitor.VisitStyles(this);
Dictionary