1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073 |
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Drawing.Drawing2D;
- using System.Text;
- namespace FastReport.Utils
- {
- public class HtmlTextRenderer : IDisposable
- {
- #region Definitions
- /// <summary>
- /// Context of HTML rendering
- /// It is better to put this structure instead of class' private fields.
- /// For future optimization. Then we can avoid constructor with dozen arguments
- /// </summary>
- public struct RendererContext
- {
- internal string text;
- internal IGraphics g;
- internal FontFamily font;
- internal float size;
- internal FontStyle style; // no keep
- internal Color color; // no keep
- internal Color underlineColor;
- internal RectangleF rect;
- internal bool underlines;
- internal StringFormat format; // no keep
- internal HorzAlign horzAlign;
- internal VertAlign vertAlign;
- internal ParagraphFormat paragraphFormat;
- internal bool forceJustify;
- internal float scale;
- internal float fontScale;
- internal InlineImageCache cache;
- internal bool isPrinting;
- internal bool isDifferentTabPositions;
- internal bool keepLastLineSpace; // Classic objects need false, translated objects need true
- }
- #endregion
- #region Internal Fields
- public static readonly System.Globalization.CultureInfo CultureInfo = System.Globalization.CultureInfo.InvariantCulture;
- #endregion Internal Fields
- #region Private Fields
- private const char SOFT_ENTER = '\u2028';
- private List<RectangleFColor> backgrounds;
- private InlineImageCache cache;
- private RectangleF displayRect;
- private bool everUnderlines;
- private FontFamily font;
- private float fontLineHeight;
- private float scale;
- private bool forceJustify;
- private StringFormat format;
- private IGraphics graphics;
- private HorzAlign horzAlign;
- private ParagraphFormat paragraphFormat;
- private List<HtmlTextRenderer.Paragraph> paragraphs;
- private bool rightToLeft;
- private float size;
- private List<LineFColor> strikeouts;
- private string text;
- private Color underlineColor;
- private List<LineFColor> underlines;
- private VertAlign vertAlign;
- private StyleDescriptor initalStyle;
- private float fontScale;
- private FastString cacheString = new FastString(100);
- private bool isPrinting;
- private bool isDifferentTabPositions;
- internal bool keepLastSpace = false;
- #endregion Private Fields
- #region Public Properties
- public IEnumerable<RectangleFColor> Backgrounds { get { return backgrounds; } }
- public RectangleF DisplayRect { get { return displayRect; } }
- public float Scale { get { return scale; } }
- public float FontScale { get { return fontScale; } set { fontScale = value; } }
- public HorzAlign HorzAlign { get { return horzAlign; } }
- public ParagraphFormat ParagraphFormat { get { return paragraphFormat; } }
- public IEnumerable<Paragraph> Paragraphs { get { return paragraphs; } }
- public bool RightToLeft
- {
- get { return rightToLeft; }
- }
- public IEnumerable<LineFColor> Stikeouts { get { return strikeouts; } }
- public float[] TabPositions
- {
- get
- {
- float firstTabStop;
- return format.GetTabStops(out firstTabStop);
- }
- }
- public float TabSize
- {
- get
- {
- // re fix tab offset #2823 sorry linux users, on linux firstTab is firstTab not tabSizes[0]
- float[] tabSizes = TabPositions;
- if (tabSizes.Length > 1)
- return tabSizes[1];
- return 0;
- }
- }
- public float TabOffset
- {
- get
- {
- // re fix tab offset #2823 sorry linux users, on linux firstTab is firstTab not tabSizes[0]
- float[] tabSizes = TabPositions;
- if (tabSizes.Length > 0)
- return tabSizes[0];
- return 0;
- }
- }
- public IEnumerable<LineFColor> Underlines { get { return underlines; } }
- public bool WordWrap
- {
- get { return (format.FormatFlags & StringFormatFlags.NoWrap) == 0; }
- }
- #endregion Public Properties
- ////TODO this is a problem with dotnet, because typographic width
- ////float width_dotnet = 2.7f;
- #region Public Constructors
- /// <summary>
- /// Contexted version of HTML renderer
- /// </summary>
- /// <param name="context"></param>
- public HtmlTextRenderer(RendererContext context)
- {
- this.text = context.text;
- this.graphics = context.g;
- this.font = context.font;
- this.size = context.size;
- this.underlineColor = context.underlineColor;
- this.displayRect = context.rect;
- this.everUnderlines = context.underlines;
- this.format = context.format;
- this.horzAlign = context.horzAlign;
- this.vertAlign = context.vertAlign;
- this.paragraphFormat = context.paragraphFormat;
- this.forceJustify = context.forceJustify;
- this.scale = context.scale;
- this.fontScale = context.fontScale;
- this.cache = context.cache;
- this.isPrinting = context.isPrinting;
- this.isDifferentTabPositions = context.isDifferentTabPositions;
- this.keepLastSpace = context.keepLastLineSpace;
- paragraphs = new List<HtmlTextRenderer.Paragraph>();
- rightToLeft = (context.format.FormatFlags & StringFormatFlags.DirectionRightToLeft) == StringFormatFlags.DirectionRightToLeft;
- // Dispose it
- this.format = StringFormat.GenericTypographic.Clone() as StringFormat;
- if (RightToLeft)
- this.format.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
- float firstTab;
- float[] tabs = context.format.GetTabStops(out firstTab);
- this.format.SetTabStops(firstTab, tabs);
- this.format.Alignment = StringAlignment.Near;
- this.format.LineAlignment = StringAlignment.Near;
- this.format.Trimming = StringTrimming.None;
- this.format.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.None;
- //FFormat.DigitSubstitutionMethod = StringDigitSubstitute.User;
- //FFormat.DigitSubstitutionLanguage = 0;
- this.format.FormatFlags |= StringFormatFlags.NoClip | StringFormatFlags.FitBlackBox | StringFormatFlags.LineLimit;
- //FFormat.FormatFlags |= StringFormatFlags.NoFontFallback;
- backgrounds = new List<RectangleFColor>();
- this.underlines = new List<LineFColor>();
- strikeouts = new List<LineFColor>();
- //FDisplayRect.Width -= width_dotnet * scale;
- initalStyle = new StyleDescriptor(context.style, context.color, BaseLine.Normal, this.font, this.size * this.fontScale);
- using (Font f = initalStyle.GetFont())
- {
- fontLineHeight = f.GetHeight(graphics.Graphics);
- }
- StringFormatFlags saveFlags = this.format.FormatFlags;
- StringTrimming saveTrimming = this.format.Trimming;
- // if word wrap is set, ignore trimming
- if (WordWrap)
- this.format.Trimming = StringTrimming.Word;
- SplitToParagraphs(text);
- AdjustParagraphLines();
- // restore original values
- displayRect = context.rect;
- this.format.FormatFlags = saveFlags;
- this.format.Trimming = saveTrimming;
- }
- public HtmlTextRenderer(string text, IGraphics g, FontFamily font, float size,
- FontStyle style, Color color, Color underlineColor, RectangleF rect, bool underlines,
- StringFormat format, HorzAlign horzAlign, VertAlign vertAlign,
- ParagraphFormat paragraphFormat, bool forceJustify, float scale, float fontScale, InlineImageCache cache, bool isPrinting = false, bool isDifferentTabPositions = false)
- {
- this.cache = cache;
- this.scale = scale;
- this.fontScale = fontScale;
- paragraphs = new List<HtmlTextRenderer.Paragraph>();
- this.text = text;
- graphics = g;
- this.font = font;
- displayRect = rect;
- rightToLeft = (format.FormatFlags & StringFormatFlags.DirectionRightToLeft) == StringFormatFlags.DirectionRightToLeft;
- // Dispose it
- this.format = StringFormat.GenericTypographic.Clone() as StringFormat;
- if (RightToLeft)
- this.format.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
- float firstTab;
- float[] tabs = format.GetTabStops(out firstTab);
- this.format.SetTabStops(firstTab, tabs);
- this.format.Alignment = StringAlignment.Near;
- this.format.LineAlignment = StringAlignment.Near;
- this.format.Trimming = StringTrimming.None;
- this.format.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.None;
- this.underlineColor = underlineColor;
- //FFormat.DigitSubstitutionMethod = StringDigitSubstitute.User;
- //FFormat.DigitSubstitutionLanguage = 0;
- this.format.FormatFlags |= StringFormatFlags.NoClip | StringFormatFlags.FitBlackBox | StringFormatFlags.LineLimit;
- //FFormat.FormatFlags |= StringFormatFlags.NoFontFallback;
- this.horzAlign = horzAlign;
- this.vertAlign = vertAlign;
- this.paragraphFormat = paragraphFormat;
- this.font = font;
- this.size = size;
- this.isPrinting = isPrinting;
- this.isDifferentTabPositions = isDifferentTabPositions;
- everUnderlines = underlines;
- backgrounds = new List<RectangleFColor>();
- this.underlines = new List<LineFColor>();
- strikeouts = new List<LineFColor>();
- //FDisplayRect.Width -= width_dotnet * scale;
- initalStyle = new StyleDescriptor(style, color, BaseLine.Normal, this.font, this.size * this.fontScale);
- using (Font f = initalStyle.GetFont())
- {
- fontLineHeight = f.GetHeight(g.Graphics);
- }
- this.forceJustify = forceJustify;
- StringFormatFlags saveFlags = this.format.FormatFlags;
- StringTrimming saveTrimming = this.format.Trimming;
- // if word wrap is set, ignore trimming
- if (WordWrap)
- this.format.Trimming = StringTrimming.Word;
- SplitToParagraphs(text);
- AdjustParagraphLines();
- // restore original values
- displayRect = rect;
- this.format.FormatFlags = saveFlags;
- this.format.Trimming = saveTrimming;
- }
- #endregion Public Constructors
- #region Public Methods
- public void AddUnknownWord(List<CharWithIndex> w, Paragraph paragraph, StyleDescriptor style, int charIndex, ref Line line, ref Word word, ref float width)
- {
- if (w[0].Char == ' ')
- {
- if (word == null || word.Type == WordType.Normal)
- {
- word = new Word(this, line, WordType.WhiteSpace);
- line.Words.Add(word);
- }
- Run r = new RunText(this, word, style, w, width, charIndex);
- word.Runs.Add(r);
- width += r.Width;
- if (width > displayRect.Width)
- line = WrapLine(paragraph, line, charIndex, displayRect.Width, ref width);
- }
- else
- {
- if (word == null || word.Type != WordType.Normal)
- {
- word = new Word(this, line, WordType.Normal);
- line.Words.Add(word);
- }
- Run r = new RunText(this, word, style, w, width, charIndex);
- word.Runs.Add(r);
- width += r.Width;
- if (width > displayRect.Width)
- line = WrapLine(paragraph, line, charIndex, displayRect.Width, ref width);
- }
- }
- public float CalcHeight()
- {
- int charsFit = 0;
- return CalcHeight(out charsFit);
- }
- public float CalcHeight(out int charsFit)
- {
- charsFit = -1;
- float height = 0;
- float displayHeight = displayRect.Height;
- float lineSpacing = 0;
- foreach (Paragraph paragraph in paragraphs)
- {
- foreach (Line line in paragraph.Lines)
- {
- line.CalcMetrics();
- height += line.Height;
- if (charsFit < 0 && height > displayHeight)
- {
- charsFit = line.OriginalCharIndex;
- }
- height += lineSpacing = line.LineSpacing;
- }
- }
- if(!keepLastSpace) // It looks like TextProcessors keep this value for every line.
- height -= lineSpacing;
- if (charsFit < 0)
- charsFit = text.Length;
- return height;
- }
- public float CalcWidth()
- {
- float width = 0;
- foreach (Paragraph paragraph in paragraphs)
- {
- foreach (Line line in paragraph.Lines)
- {
- if (width < line.Width)
- width = line.Width;
- }
- }
- return width;
- }
- #endregion Public Methods
- #region Internal Methods
- /// <summary>
- /// Returns splited string
- /// </summary>
- /// <param name="text">text for splitting</param>
- /// <param name="charactersFitted">index of first character of second string</param>
- /// <param name="result">second part of string</param>
- /// <param name="endOnEnter">returns true if ends on enter</param>
- /// <returns>first part of string</returns>
- internal static string BreakHtml(string text, int charactersFitted, out string result, out bool endOnEnter)
- {
- endOnEnter = false;
- Stack<SimpleFastReportHtmlElement> elements = new Stack<SimpleFastReportHtmlElement>();
- SimpleFastReportHtmlReader reader = new SimpleFastReportHtmlReader(text);
- while (reader.IsNotEOF)
- {
- if (reader.Position >= charactersFitted)
- {
- StringBuilder firstPart = new StringBuilder();
- if (reader.Character.Char == SOFT_ENTER)
- firstPart.Append(text.Substring(0, reader.LastPosition));
- else
- firstPart.Append(text.Substring(0, reader.Position));
- foreach (SimpleFastReportHtmlElement el in elements)
- {
- SimpleFastReportHtmlElement el2 = new SimpleFastReportHtmlElement(el.name, true);
- firstPart.Append(el2.ToString());
- }
- SimpleFastReportHtmlElement[] arr = elements.ToArray();
- StringBuilder secondPart = new StringBuilder();
- for (int i = arr.Length - 1; i >= 0; i--)
- secondPart.Append(arr[i].ToString());
- secondPart.Append(text.Substring(reader.Position));
- endOnEnter = reader.Character.Char == '\n';
- result = secondPart.ToString();
- return firstPart.ToString();
- }
- if (!reader.Read())
- {
- if (reader.Element.isEnd)
- {
- int enumIndex = 1;
- using (Stack<SimpleFastReportHtmlElement>.Enumerator enumerator = elements.GetEnumerator())
- {
- while (enumerator.MoveNext())
- {
- SimpleFastReportHtmlElement el = enumerator.Current;
- if (el.name == reader.Element.name)
- {
- for (int i = 0; i < enumIndex; i++)
- elements.Pop();
- break;
- }
- else
- enumIndex++;
- }
- }
- }
- else if (!reader.Element.IsSelfClosed) elements.Push(reader.Element);
- }
- }
- result = "";
- return text;
- }
- internal void Draw()
- {
- // set clipping
- IGraphicsState state = graphics.Save();
- //RectangleF rect = new RectangleF(FDisplayRect.Location, SizeF.Add(FDisplayRect.Size, new SizeF(width_dotnet, 0)));
- //FGraphics.SetClip(rect, CombineMode.Intersect);
- graphics.SetClip(displayRect, CombineMode.Intersect);
- // reset alignment
- //StringAlignment saveAlign = FFormat.Alignment;
- //StringAlignment saveLineAlign = FFormat.LineAlignment;
- //FFormat.Alignment = StringAlignment.Near;
- //FFormat.LineAlignment = StringAlignment.Near;
- //if (FRightToLeft)
- // foreach (RectangleFColor rect in FBackgrounds)
- // using (Brush brush = new SolidBrush(rect.Color))
- // FGraphics.FillRectangle(brush, rect.Left - rect.Width, rect.Top, rect.Width, rect.Height);
- //else
- foreach (RectangleFColor rect in backgrounds)
- using (Brush brush = new SolidBrush(rect.Color))
- graphics.FillRectangle(brush, rect.Left, rect.Top, rect.Width, rect.Height);
- foreach (Paragraph p in paragraphs)
- foreach (Line l in p.Lines)
- {
- //#if DEBUG
- // FGraphics.DrawRectangle(Pens.Blue, FDisplayRect.Left, l.Top, FDisplayRect.Width, l.Height);
- //#endif
- foreach (Word w in l.Words)
- switch (w.Type)
- {
- case WordType.Normal:
- foreach (Run r in w.Runs)
- {
- r.Draw();
- }
- break;
- }
- }
- //if (RightToLeft)
- //{
- // foreach (LineFColor line in FUnderlines)
- // using (Pen pen = new Pen(line.Color, line.Width))
- // FGraphics.DrawLine(pen, 2 * line.Left - line.Right, line.Top, line.Left, line.Top);
- // foreach (LineFColor line in FStrikeouts)
- // using (Pen pen = new Pen(line.Color, line.Width))
- // FGraphics.DrawLine(pen, 2 * line.Left - line.Right, line.Top, line.Left, line.Top);
- //}
- //else
- //{
- foreach (LineFColor line in underlines)
- using (Pen pen = new Pen(line.Color, line.Width))
- graphics.DrawLine(pen, line.Left, line.Top, line.Right, line.Top);
- foreach (LineFColor line in strikeouts)
- using (Pen pen = new Pen(line.Color, line.Width))
- graphics.DrawLine(pen, line.Left, line.Top, line.Right, line.Top);
- //}
- // restore alignment and clipping
- //FFormat.Alignment = saveAlign;
- //FFormat.LineAlignment = saveLineAlign;
- graphics.Restore(state);
- }
- #endregion Internal Methods
- #region Private Methods
- private void AdjustParagraphLines()
- {
- // calculate text height
- float height = 0;
- height = CalcHeight();
- // calculate Y offset
- float offsetY = displayRect.Top;
- if (vertAlign == VertAlign.Center)
- offsetY += (displayRect.Height - height) / 2;
- else if (vertAlign == VertAlign.Bottom)
- offsetY += (displayRect.Height - height) - 1;
- for (int i = 0; i < paragraphs.Count; i++)
- {
- Paragraph paragraph = paragraphs[i];
- paragraph.AlignLines(i == paragraphs.Count - 1 && forceJustify);
- // adjust line tops
- foreach (Line line in paragraph.Lines)
- {
- line.Top = offsetY;
- line.MakeUnderlines();
- line.MakeStrikeouts();
- line.MakeBackgrounds();
- offsetY += line.Height + line.LineSpacing;
- }
- }
- }
- private void CssStyle(StyleDescriptor style, Dictionary<string, string> dict)
- {
- if (dict == null)
- return;
- string tStr;
- if (dict.TryGetValue("font-size", out tStr))
- {
- if (EndsWith(tStr, "px"))
- try { style.Size = fontScale * 0.75f * Single.Parse(tStr.Substring(0, tStr.Length - 2), CultureInfo); } catch { }
- else if (EndsWith(tStr, "pt"))
- try { style.Size = fontScale * Single.Parse(tStr.Substring(0, tStr.Length - 2), CultureInfo); } catch { }
- else if (EndsWith(tStr, "em"))
- try { style.Size *= Single.Parse(tStr.Substring(0, tStr.Length - 2), CultureInfo); } catch { }
- }
- if (dict.TryGetValue("font-family", out tStr))
- style.Font = new FontFamily(tStr);
- if (dict.TryGetValue("color", out tStr))
- {
- if (StartsWith(tStr, "#"))
- try { style.Color = Color.FromArgb((int)(0xFF000000 + uint.Parse(tStr.Substring(1), System.Globalization.NumberStyles.HexNumber))); } catch { }
- else if (StartsWith(tStr, "rgba"))
- {
- int i1 = tStr.IndexOf('(');
- int i2 = tStr.IndexOf(')');
- string[] strs = tStr.Substring(i1 + 1, i2 - i1 - 1).Split(',');
- if (strs.Length == 4)
- {
- float r, g, b, a;
- try
- {
- r = Single.Parse(strs[0], CultureInfo);
- g = Single.Parse(strs[1], CultureInfo);
- b = Single.Parse(strs[2], CultureInfo);
- a = Single.Parse(strs[3], CultureInfo);
- style.Color = Color.FromArgb((int)(a * 0xFF), (int)r, (int)g, (int)b);
- }
- catch { }
- }
- }
- else if (StartsWith(tStr, "rgb"))
- {
- int i1 = tStr.IndexOf('(');
- int i2 = tStr.IndexOf(')');
- string[] strs = tStr.Substring(i1 + 1, i2 - i1 - 1).Split(',');
- if (strs.Length == 3)
- {
- float r, g, b;
- try
- {
- r = Single.Parse(strs[0], CultureInfo);
- g = Single.Parse(strs[1], CultureInfo);
- b = Single.Parse(strs[2], CultureInfo);
- style.Color = Color.FromArgb((int)r, (int)g, (int)b);
- }
- catch { }
- }
- }
- else style.Color = Color.FromName(tStr);
- }
- if (dict.TryGetValue("background-color", out tStr))
- {
- if (StartsWith(tStr, "#"))
- try { style.BackgroundColor = Color.FromArgb((int)(0xFF000000 + uint.Parse(tStr.Substring(1), System.Globalization.NumberStyles.HexNumber))); } catch { }
- else if (StartsWith(tStr, "rgba"))
- {
- int i1 = tStr.IndexOf('(');
- int i2 = tStr.IndexOf(')');
- string[] strs = tStr.Substring(i1 + 1, i2 - i1 - 1).Split(',');
- if (strs.Length == 4)
- {
- float r, g, b, a;
- try
- {
- r = Single.Parse(strs[0], CultureInfo);
- g = Single.Parse(strs[1], CultureInfo);
- b = Single.Parse(strs[2], CultureInfo);
- a = Single.Parse(strs[3], CultureInfo);
- style.BackgroundColor = Color.FromArgb((int)(a * 0xFF), (int)r, (int)g, (int)b);
- }
- catch { }
- }
- }
- else if (StartsWith(tStr, "rgb"))
- {
- int i1 = tStr.IndexOf('(');
- int i2 = tStr.IndexOf(')');
- string[] strs = tStr.Substring(i1 + 1, i2 - i1 - 1).Split(',');
- if (strs.Length == 3)
- {
- float r, g, b;
- try
- {
- r = Single.Parse(strs[0], CultureInfo);
- g = Single.Parse(strs[1], CultureInfo);
- b = Single.Parse(strs[2], CultureInfo);
- style.BackgroundColor = Color.FromArgb((int)r, (int)g, (int)b);
- }
- catch { }
- }
- }
- else style.BackgroundColor = Color.FromName(tStr);
- }
- }
- private bool EndsWith(string str1, string str2)
- {
- int len1 = str1.Length;
- int len2 = str2.Length;
- if (len1 < len2) return false;
- switch (len2)
- {
- case 0: return true;
- case 1: return str1[len1 - 1] == str2[len2 - 1];
- case 2: return str1[len1 - 1] == str2[len2 - 1] && str1[len1 - 2] == str2[len2 - 2];
- case 3: return str1[len1 - 1] == str2[len2 - 1] && str1[len1 - 2] == str2[len2 - 2] && str1[len1 - 3] == str2[len2 - 3];
- case 4: return str1[len1 - 1] == str2[len2 - 1] && str1[len1 - 2] == str2[len2 - 2] && str1[len1 - 3] == str2[len2 - 3] && str1[len1 - 4] == str2[len2 - 4];
- default: return str1.EndsWith(str2);
- }
- }
- private float GetTabPosition(float pos)
- {
- float tabOffset = TabOffset;
- float tabSize = TabSize;
- int tabPosition = (int)((pos - tabOffset) / tabSize);
- if (pos < tabOffset)
- return tabOffset;
- return (tabPosition + 1) * tabSize + tabOffset;
- }
- private float GetTabPosition(float pos, int tabIndex)
- {
- float tabOffset = 0;
- float tabSize;
- float firstTabOffset;
- float[] tabsPos = format.GetTabStops(out firstTabOffset);
- if (tabIndex <= 1)
- {
- tabOffset = TabOffset;
- tabSize = TabSize;
- }
- else
- {
- for (int i = 0; i < tabIndex; i++)
- {
- tabOffset += tabsPos[i];
- }
- tabSize = tabsPos[tabIndex];
- }
- int tabPosition = (int)((pos - tabOffset) / tabSize);
- if (pos < tabOffset)
- return tabOffset;
- return (tabPosition + 1) * tabSize + tabOffset;
- }
- private void SplitToParagraphs(string text)
- {
- Stack<SimpleFastReportHtmlElement> elements = new Stack<SimpleFastReportHtmlElement>();
- SimpleFastReportHtmlReader reader = new SimpleFastReportHtmlReader(this.text);
- List<CharWithIndex> currentWord = new List<CharWithIndex>();
- float width = paragraphFormat.SkipFirstLineIndent ? 0 : paragraphFormat.FirstLineIndent;
- Paragraph paragraph = new Paragraph(this);
- int charIndex = 0;
- int tabIndex = 0;
- Line line = new Line(this, paragraph, charIndex);
- paragraph.Lines.Add(line);
- paragraphs.Add(paragraph);
- Word word = null;
- StyleDescriptor style = new StyleDescriptor(initalStyle);
- //bool softReturn = false;
- //CharWithIndex softReturnChar = new CharWithIndex();
- while (reader.IsNotEOF)
- {
- if (reader.Read())
- {
- switch (reader.Character.Char)
- {
- case ' ':
- if (word == null)
- {
- word = new Word(this, line, WordType.WhiteSpace);
- line.Words.Add(word);
- }
- if (word.Type == WordType.WhiteSpace)
- currentWord.Add(reader.Character);
- else
- {
- if (currentWord.Count > 0)
- {
- Run r = new RunText(this, word, style, currentWord, width, charIndex);
- word.Runs.Add(r);
- currentWord.Clear();
- width += r.Width;
- if (width > displayRect.Width)
- line = WrapLine(paragraph, line, charIndex, displayRect.Width, ref width);
- }
- currentWord.Add(reader.Character);
- word = new Word(this, line, WordType.WhiteSpace);
- line.Words.Add(word);
- charIndex = reader.LastPosition;
- }
- break;
- case '\t':
- if (word != null)
- {
- if (currentWord.Count > 0)
- {
- Run r = new RunText(this, word, style, currentWord, width, charIndex);
- word.Runs.Add(r);
- currentWord.Clear();
- width += r.Width;
- if (width > displayRect.Width)
- line = WrapLine(paragraph, line, charIndex, displayRect.Width, ref width);
- }
- }
- else
- {
- if (currentWord.Count > 0)
- {
- AddUnknownWord(currentWord, paragraph, style, charIndex, ref line, ref word, ref width);
- }
- }
- charIndex = reader.LastPosition;
- word = new Word(this, line, WordType.Tab);
-
- Run tabRun = new RunText(this, word, style, new List<CharWithIndex>(new CharWithIndex[] { reader.Character }), width, charIndex);
- word.Runs.Add(tabRun);
- float width2 = GetTabPosition(width);
- if (isDifferentTabPositions)
- {
- width2 = GetTabPosition(width, tabIndex);
- }
- if (width2 < width) width2 = width;
- if (line.Words.Count > 0 && width2 > displayRect.Width)
- {
- tabRun.Left = 0;
- line = new Line(this, paragraph, charIndex);
- tabIndex = 0;
- paragraph.Lines.Add(line);
- width = 0;
- width2 = GetTabPosition(width);
- if (isDifferentTabPositions)
- {
- width2 = GetTabPosition(width, tabIndex);
- }
- }
- tabIndex++;
- line.Words.Add(word);
- tabRun.Width = width2 - width;
- width = width2;
- word = null;
- break;
- case SOFT_ENTER://soft enter
- if (word != null)
- {
- if (currentWord.Count > 0)
- {
- Run r = new RunText(this, word, style, currentWord, width, charIndex);
- word.Runs.Add(r);
- currentWord.Clear();
- width += r.Width;
- if (width > displayRect.Width)
- line = WrapLine(paragraph, line, charIndex, displayRect.Width, ref width);
- }
- }
- else
- {
- if (currentWord.Count > 0)
- {
- AddUnknownWord(currentWord, paragraph, style, charIndex, ref line, ref word, ref width);
- }
- }
- charIndex = reader.Position;
- //currentWord.Append(' ')
- //RunText runText = new RunText(this, word, style, new List<CharWithIndex>(new CharWithIndex[] { reader.Character }), width, charIndex);
- //runText.Width = 0;
- //word.Runs.Add(runText);
- line = new Line(this, paragraph, charIndex);
- word = null;
- width = 0;
- currentWord.Clear();
- paragraph.Lines.Add(line);
- break;
- case '\n':
- if (word != null)
- {
- if (currentWord.Count > 0)
- {
- Run r = new RunText(this, word, style, currentWord, width, charIndex);
- word.Runs.Add(r);
- currentWord.Clear();
- width += r.Width;
- if (width > displayRect.Width)
- line = WrapLine(paragraph, line, charIndex, displayRect.Width, ref width);
- }
- }
- else
- {
- if (currentWord.Count > 0)
- {
- AddUnknownWord(currentWord, paragraph, style, charIndex, ref line, ref word, ref width);
- }
- }
- charIndex = reader.Position;
- paragraph = new Paragraph(this);
- paragraphs.Add(paragraph);
- line = new Line(this, paragraph, charIndex);
- word = null;
- width = paragraphFormat.FirstLineIndent;
- paragraph.Lines.Add(line);
- break;
- case '\r'://ignore
- break;
- default:
- if (word == null)
- {
- word = new Word(this, line, WordType.Normal);
- line.Words.Add(word);
- }
- if (word.Type == WordType.Normal)
- currentWord.Add(reader.Character);
- else
- {
- if (currentWord.Count > 0)
- {
- Run r = new RunText(this, word, style, currentWord, width, charIndex);
- word.Runs.Add(r);
- currentWord.Clear();
- width += r.Width;
- if (width > displayRect.Width)
- line = WrapLine(paragraph, line, charIndex, displayRect.Width, ref width);
- }
- currentWord.Add(reader.Character);
- word = new Word(this, line, WordType.Normal);
- line.Words.Add(word);
- charIndex = reader.LastPosition;
- }
- break;
- }
- }
- else
- {
- StyleDescriptor newStyle = new StyleDescriptor(initalStyle);
- SimpleFastReportHtmlElement element = reader.Element;
- if (!element.IsSelfClosed)
- {
- if (element.isEnd)
- {
- int enumIndex = 1;
- using (Stack<SimpleFastReportHtmlElement>.Enumerator enumerator = elements.GetEnumerator())
- {
- while (enumerator.MoveNext())
- {
- SimpleFastReportHtmlElement el = enumerator.Current;
- if (el.name == element.name)
- {
- for (int i = 0; i < enumIndex; i++)
- elements.Pop();
- break;
- }
- else
- enumIndex++;
- }
- }
- }
- else elements.Push(element);
- SimpleFastReportHtmlElement[] arr = elements.ToArray();
- for (int i = arr.Length - 1; i >= 0; i--)
- {
- SimpleFastReportHtmlElement el = arr[i];
- switch (el.name)
- {
- case "b":
- newStyle.FontStyle |= FontStyle.Bold;
- break;
- case "i":
- newStyle.FontStyle |= FontStyle.Italic;
- break;
- case "u":
- newStyle.FontStyle |= FontStyle.Underline;
- break;
- case "sub":
- newStyle.BaseLine = BaseLine.Subscript;
- break;
- case "sup":
- newStyle.BaseLine = BaseLine.Superscript;
- break;
- case "strike":
- newStyle.FontStyle |= FontStyle.Strikeout;
- break;
- //case "font":
- // {
- // string color = null;
- // string face = null;
- // string size = null;
- // if (el.Attributes != null)
- // {
- // el.Attributes.TryGetValue("color", out color);
- // el.Attributes.TryGetValue("face", out face);
- // el.Attributes.TryGetValue("size", out size);
- // }
- // if (color != null)
- // {
- // if (color.StartsWith("\"") && color.EndsWith("\""))
- // color = color.Substring(1, color.Length - 2);
- // if (color.StartsWith("#"))
- // {
- // newStyle.Color = Color.FromArgb((int)(0xFF000000 + uint.Parse(color.Substring(1), System.Globalization.NumberStyles.HexNumber)));
- // }
- // else
- // {
- // newStyle.Color = Color.FromName(color);
- // }
- // }
- // if (face != null)
- // newStyle.Font = face;
- // if (size != null)
- // {
- // try
- // {
- // size = size.Trim(' ');
- // newStyle.Size = (float)Converter.FromString(typeof(float), size) * FFontScale;
- // }
- // catch
- // {
- // newStyle.Size = FSize * FFontScale;
- // }
- // }
- // }
- // break;
- }
- CssStyle(newStyle, el.Style);
- }
- if (currentWord.Count > 0)
- {
- AddUnknownWord(currentWord, paragraph, style, charIndex, ref line, ref word, ref width);
- currentWord.Clear();
- charIndex = reader.LastPosition;
- }
- style = newStyle;
- }
- else
- {
- switch (element.name)
- {
- case "img":
- if (element.attributes != null && element.attributes.ContainsKey("src"))
- {
- float img_width = -1;
- float img_height = -1;
- string tStr;
- if (element.attributes.TryGetValue("width", out tStr))
- try { img_width = Single.Parse(tStr, System.Globalization.CultureInfo.InstalledUICulture); } catch { }
- if (element.attributes.TryGetValue("height", out tStr))
- try { img_height = Single.Parse(tStr, System.Globalization.CultureInfo.InstalledUICulture); } catch { }
- if (currentWord.Count > 0)
- {
- AddUnknownWord(currentWord, paragraph, style, charIndex, ref line, ref word, ref width);
- currentWord.Clear();
- }
- if (word == null || word.Type != WordType.Normal)
- {
- word = new Word(this, line, WordType.Normal);
- line.Words.Add(word);
- charIndex = reader.LastPosition;
- }
- Run r = new RunImage(this, word, element.attributes["src"], style, width, reader.LastPosition, img_width, img_height);
- word.Runs.Add(r);
- width += r.Width;
- if (width > displayRect.Width)
- line = WrapLine(paragraph, line, charIndex, displayRect.Width, ref width);
- }
- break;
- }
- }
- }
- }
- if (currentWord.Count > 0)
- {
- AddUnknownWord(currentWord, paragraph, style, charIndex, ref line, ref word, ref width);
- }
- }
- private bool StartsWith(string str1, string str2)
- {
- if (str1.Length < str2.Length) return false;
- switch (str2.Length)
- {
- case 0: return true;
- case 1: return str1[0] == str2[0];
- case 2: return str1[0] == str2[0] && str1[1] == str2[1];
- case 3: return str1[0] == str2[0] && str1[1] == str2[1] && str1[2] == str2[2];
- case 4: return str1[0] == str2[0] && str1[1] == str2[1] && str1[2] == str2[2] && str1[3] == str2[3];
- default: return str1.StartsWith(str2);
- }
- }
- /// <summary>
- /// Check the line, and if last word is able to move next line, move it.
- /// e.g. white space won't move to next line.
- /// If word is not moved return current line.
- /// else return new line
- /// </summary>
- /// <param name="paragraph">the paragraph for lines</param>
- /// <param name="line">the line with extra words</param>
- /// <param name="wordCharIndex">the index of start last word in this line</param>
- /// <param name="availableWidth">width to place words</param>
- /// <param name="newWidth">ref to current line width</param>
- /// <returns></returns>
- private Line WrapLine(Paragraph paragraph, Line line, int wordCharIndex, float availableWidth, ref float newWidth)
- {
- if (line.Words.Count == 0)
- {
- return line;
- }
- if (line.Words.Count == 1 && line.Words[0].Type == WordType.Normal)
- {
- Word word = line.Words[0];
- float width = word.Runs.Count > 0 ? word.Runs[0].Left : 0;
- /* Foreach runs, while run in available space next run
- * if run begger then space split run and generate new word and run
- */
- Word newWord = new Word(word.Renderer, line, word.Type);
- line.Words.Clear();
- line.Words.Add(newWord);
- foreach (Run run in word.Runs)
- {
- width += run.Width;
- if (width <= availableWidth || availableWidth < 0)
- {
- newWord.Runs.Add(run);
- run.Word = newWord;
- }
- else
- {
- Run secondPart = run;
- while (secondPart != null)
- {
- Run firstPart = secondPart.Split(availableWidth - run.Left, out secondPart);
- if (firstPart != null)
- {
- newWord.Runs.Clear();
- newWord.Runs.Add(firstPart);
- firstPart.Word = newWord;
- }
- else if (newWord.Runs.Count == 0)
- {
- newWord.Runs.Add(run);
- run.Word = newWord;
- secondPart = null;
- }
- if (secondPart != null)
- {
- line = new Line(line.Renderer, paragraph, secondPart.CharIndex);
- paragraph.Lines.Add(line);
- newWord = new Word(newWord.Renderer, line, newWord.Type);
- line.Words.Add(newWord);
- secondPart.Left = 0;
- width = secondPart.Width;
- secondPart.Word = newWord;
- newWord.Runs.Add(secondPart);
- if (width < availableWidth)
- secondPart = null;
- }
- }
- }
- }
- return line;
- }
- else
- if (line.Words[line.Words.Count - 1].Type == WordType.WhiteSpace)
- {
- return line;
- }
- else
- {
- Word lastWord = line.Words[line.Words.Count - 1];
- line.Words.RemoveAt(line.Words.Count - 1);
- Line result = new Line(this, paragraph, wordCharIndex);
- paragraph.Lines.Add(result);
- newWidth = 0;
- result.Words.Add(lastWord);
- lastWord.Line = result;
- foreach (Run r in lastWord.Runs)
- {
- r.Left = newWidth;
- newWidth += r.Width;
- }
- return result;
- }
- }
- #endregion Private Methods
- #region Public Enums
- public enum WordType
- {
- Normal,
- WhiteSpace,
- Tab,
- }
- #endregion Public Enums
- #region Internal Enums
- /// <summary>
- /// Represents character placement.
- /// </summary>
- public enum BaseLine
- {
- Normal,
- Subscript,
- Superscript
- }
- #endregion Internal Enums
- #region Public Structs
- public struct CharWithIndex
- {
- #region Public Fields
- public char Char;
- public int Index;
- #endregion Public Fields
- #region Public Constructors
- public CharWithIndex(char v, int fPosition)
- {
- this.Char = v;
- this.Index = fPosition;
- }
- #endregion Public Constructors
- }
- #if READONLY_STRUCTS
- public readonly struct LineFColor
- #else
- public struct LineFColor
- #endif
- {
- #region Public Fields
- public readonly Color Color;
- public readonly float Left;
- public readonly float Right;
- public readonly float Top;
- public readonly float Width;
- #endregion Public Fields
- #region Public Constructors
- public LineFColor(float left, float top, float right, float width, Color color)
- {
- this.Left = left;
- this.Top = top;
- this.Right = right;
- this.Width = width;
- this.Color = color;
- }
- public LineFColor(float left, float top, float right, float width, byte R, byte G, byte B)
- : this(left, top, right, width, Color.FromArgb(R, G, B))
- {
- }
- public LineFColor(float left, float top, float right, float width, byte R, byte G, byte B, byte A)
- : this(left, top, right, width, Color.FromArgb(A, R, G, B))
- {
- }
- public LineFColor(float left, float top, float right, float width, int R, int G, int B)
- : this(left, top, right, width, Color.FromArgb(R, G, B))
- {
- }
- public LineFColor(float left, float top, float right, float width, int R, int G, int B, int A)
- : this(left, top, right, width, Color.FromArgb(A, R, G, B))
- {
- }
- #endregion Public Constructors
- }
- #if READONLY_STRUCTS
- public readonly struct RectangleFColor
- #else
- public struct RectangleFColor
- #endif
- {
- #region Public Fields
- public readonly Color Color;
- public readonly float Height;
- public readonly float Left;
- public readonly float Top;
- public readonly float Width;
- #endregion Public Fields
- #region Public Constructors
- public RectangleFColor(float left, float top, float width, float height, Color color)
- {
- this.Left = left;
- this.Top = top;
- this.Width = width;
- this.Height = height;
- this.Color = color;
- }
- public RectangleFColor(float left, float top, float width, float height, byte R, byte G, byte B)
- : this(left, top, width, height, Color.FromArgb(R, G, B))
- {
- }
- public RectangleFColor(float left, float top, float width, float height, byte R, byte G, byte B, byte A)
- : this(left, top, width, height, Color.FromArgb(A, R, G, B))
- {
- }
- public RectangleFColor(float left, float top, float width, float height, int R, int G, int B)
- : this(left, top, width, height, Color.FromArgb(R, G, B))
- {
- }
- public RectangleFColor(float left, float top, float width, float height, int R, int G, int B, int A)
- : this(left, top, width, height, Color.FromArgb(A, R, G, B))
- {
- }
- #endregion Public Constructors
- }
- #endregion Public Structs
- #region Public Classes
- public class Line
- {
- #region Private Fields
- private float baseLine;
- private float height;
- private HorzAlign horzAlign;
- private float lineSpacing;
- private int originalCharIndex;
- private Paragraph paragraph;
- private HtmlTextRenderer renderer;
- private float top;
- private float width;
- private List<Word> words;
- #endregion Private Fields
- #region Public Properties
- public float BaseLine
- {
- get { return baseLine; }
- set { baseLine = value; }
- }
- public float Height
- {
- get { return height; }
- set { height = value; }
- }
- public HorzAlign HorzAlign
- {
- get { return horzAlign; }
- }
- public float LineSpacing
- {
- get { return lineSpacing; }
- set { lineSpacing = value; }
- }
- public int OriginalCharIndex
- {
- get { return originalCharIndex; }
- set { originalCharIndex = value; }
- }
- public Paragraph Paragraph
- {
- get { return paragraph; }
- set { paragraph = value; }
- }
- public HtmlTextRenderer Renderer
- {
- get { return renderer; }
- }
- public float Top
- {
- get { return top; }
- set
- {
- top = value;
- foreach (Word w in Words)
- {
- foreach (Run r in w.Runs)
- {
- float shift = 0;
- if (r.Style.BaseLine == HtmlTextRenderer.BaseLine.Subscript)
- shift += r.Height * 0.45f;
- else if (r.Style.BaseLine == HtmlTextRenderer.BaseLine.Superscript)
- shift -= r.BaseLine - r.Height * 0.15f;
- r.Top = top + BaseLine - r.BaseLine + shift;
- }
- }
- }
- }
- public float Width
- {
- get
- {
- return width;
- }
- }
- public List<Word> Words
- {
- get { return words; }
- }
- #endregion Public Properties
- #region Public Constructors
- public Line(HtmlTextRenderer renderer, Paragraph paragraph, int charIndex)
- {
- words = new List<Word>();
- this.renderer = renderer;
- this.paragraph = paragraph;
- originalCharIndex = charIndex;
- }
- #endregion Public Constructors
- #region Public Methods
- public override string ToString()
- {
- return String.Format("Words[{0}]", Words.Count);
- }
- #endregion Public Methods
- #region Internal Methods
- internal void AlignWords(HorzAlign align)
- {
- horzAlign = align;
- float width = CalcWidth();
- float left = Words.Count > 0 && Words[0].Runs.Count > 0 ? Words[0].Runs[0].Left : 0;
- width += left;
- this.width = width;
- switch (align)
- {
- case HorzAlign.Left:
- break;
- case HorzAlign.Right:
- {
- float delta = Renderer.displayRect.Width - width;
- foreach (Word w in Words)
- foreach (Run r in w.Runs)
- r.Left += delta;
- }
- break;
- case HorzAlign.Center:
- {
- float delta = (Renderer.displayRect.Width - width) / 2f;
- foreach (Word w in Words)
- foreach (Run r in w.Runs)
- r.Left += delta;
- }
- break;
- case HorzAlign.Justify:
- {
- int spaces = 0;
- int tab_index = -1;
- bool isWordExistAfterTab = true;
- for (int i = 0; i < Words.Count - 1; i++)
- {
- if (isWordExistAfterTab)
- {
- if (Words[i].Type == WordType.WhiteSpace)
- foreach (Run r in Words[i].Runs)
- if (r is RunText)
- spaces += (r as RunText).Text.Length;
- }
- else if (Words[i].Type == WordType.Normal)
- isWordExistAfterTab = true;
- if (Words[i].Type == WordType.Tab)
- {
- spaces = 0;
- tab_index = i;
- isWordExistAfterTab = false;
- }
- }
- if (spaces > 0)
- {
- float space_width = (Renderer.displayRect.Width - width) / spaces;
- for (int i = 0; i < Words.Count; i++)
- {
- Word w = Words[i];
- if (w.Type == WordType.WhiteSpace)
- foreach (Run r in w.Runs)
- {
- if (i > tab_index && r is RunText)
- r.Width += space_width * (r as RunText).Text.Length;
- r.Left = left;
- left += r.Width;
- }
- else foreach (Run r in w.Runs)
- {
- r.Left = left;
- left += r.Width;
- }
- }
- }
- }
- break;
- }
- if (renderer.RightToLeft)
- {
- float rectRight = Renderer.displayRect.Right;
- foreach (Word w in Words)
- foreach (Run r in w.Runs)
- r.Left = rectRight - r.Left;
- }
- else
- {
- float rectLeft = Renderer.displayRect.Left;
- foreach (Word w in Words)
- foreach (Run r in w.Runs)
- r.Left += rectLeft;
- }
- }
- internal void CalcMetrics()
- {
- baseLine = 0;
- foreach (Word word in Words)
- {
- word.CalcMetrics();
- baseLine = Math.Max(baseLine, word.BaseLine);
- }
- height = renderer.fontLineHeight;
- float decent = 0;
- foreach (Word word in Words)
- {
- decent = Math.Max(decent, word.Descent);
- }
- if (baseLine + decent > 0.01)
- height = baseLine + decent;
- switch (renderer.paragraphFormat.LineSpacingType)
- {
- case LineSpacingType.AtLeast:
- if (height < renderer.paragraphFormat.LineSpacing)
- lineSpacing = renderer.paragraphFormat.LineSpacing - height;
- break;
- case LineSpacingType.Single:
- break;
- case LineSpacingType.Multiple:
- lineSpacing = height * (renderer.paragraphFormat.LineSpacingMultiple - 1);
- break;
- case LineSpacingType.Exactly:
- lineSpacing = renderer.paragraphFormat.LineSpacing - height;
- break;
- }
- }
- internal void MakeBackgrounds()
- {
- List<RectangleFColor> list = renderer.backgrounds;
- if (renderer.rightToLeft)
- {
- foreach (Word word in Words)
- foreach (Run run in word.Runs)
- if (run.Style.BackgroundColor.A > 0)
- list.Add(new RectangleFColor(
- run.Left - run.Width, top, run.Width, height, run.Style.BackgroundColor
- ));
- }
- else
- {
- foreach (Word word in Words)
- foreach (Run run in word.Runs)
- if (run.Style.BackgroundColor.A > 0)
- list.Add(new RectangleFColor(
- run.Left, top, run.Width, height, run.Style.BackgroundColor
- ));
- }
- }
- internal void MakeEverUnderlines()
- {
- OwnHashSet<StyleDescriptor> styles = new OwnHashSet<StyleDescriptor>();
- float size = 0;
- float underline = 0;
- foreach (Word word in Words)
- foreach (Run run in word.Runs)
- if (!styles.Contains(run.Style))
- {
- styles.Add(run.Style);
- size += run.Style.Size;
- underline += run.Descent / 2;
- }
- if (styles.Count == 0 || BaseLine <= 0.01)
- {
- using (Font ff = renderer.initalStyle.GetFont())
- {
- float lineSpace = ff.FontFamily.GetLineSpacing(renderer.initalStyle.FontStyle);
- float ascent = ff.FontFamily.GetCellAscent(renderer.initalStyle.FontStyle);
- baseLine = height * ascent / lineSpace;
- float FDescent = height - baseLine;
- underline = FDescent / 2;
- size = ff.Size;
- }
- }
- else
- {
- size /= styles.Count;
- underline /= styles.Count;
- }
- float fixScale = Renderer.Scale / Renderer.FontScale;
- renderer.underlines.Add(new LineFColor(
- renderer.displayRect.Left, Top + BaseLine + underline, renderer.displayRect.Right, size * 0.1f * fixScale, renderer.underlineColor
- ));
- }
- internal void MakeStrikeouts()
- {
- List<LineFColor> lines = renderer.strikeouts;
- float fixScale = Renderer.Scale / Renderer.FontScale;
- if (renderer.rightToLeft)
- {
- foreach (Word word in Words)
- foreach (Run r in word.Runs)
- if ((r.Style.FontStyle & FontStyle.Strikeout) == FontStyle.Strikeout)
- lines.Add(new LineFColor(
- r.Left - r.Width, r.Top + r.BaseLine / 3f * 2f, r.Left, r.Style.Size * 0.1f * fixScale,
- r.Style.Color));
- }
- else
- {
- foreach (Word word in Words)
- foreach (Run r in word.Runs)
- if ((r.Style.FontStyle & FontStyle.Strikeout) == FontStyle.Strikeout)
- lines.Add(new LineFColor(
- r.Left, r.Top + r.BaseLine / 3f * 2f, r.Left + r.Width, r.Style.Size * 0.1f * fixScale,
- r.Style.Color));
- }
- }
- internal void MakeUnderlines()
- {
- if (renderer.everUnderlines)
- {
- MakeEverUnderlines();
- return;
- }
- List<List<Run>> runs = new List<List<Run>>();
- List<Run> currentRuns = null;
- foreach (Word word in Words)
- foreach (Run run in word.Runs)
- {
- if ((run.Style.FontStyle & FontStyle.Underline) == FontStyle.Underline)
- {
- if (currentRuns == null)
- {
- currentRuns = new List<Run>();
- runs.Add(currentRuns);
- }
- currentRuns.Add(run);
- }
- else
- {
- currentRuns = null;
- }
- }
- List<LineFColor> unerlines = renderer.underlines;
- float fixScale = Renderer.Scale / Renderer.FontScale;
- foreach (List<Run> cRuns in runs)
- {
- OwnHashSet<StyleDescriptor> styles = new OwnHashSet<StyleDescriptor>();
- float size = 0;
- float underline = 0;
- foreach (Run r in cRuns)
- if (!styles.Contains(r.Style))
- {
- styles.Add(r.Style);
- size += r.Style.Size;
- underline += r.Descent / 2;
- }
- size /= styles.Count;
- underline /= styles.Count;
- if (renderer.rightToLeft)
- foreach (Run r in cRuns)
- unerlines.Add(new LineFColor(
- r.Left - r.Width, r.Top + r.BaseLine + underline, r.Left, size * 0.1f * fixScale,
- r.Style.Color));
- else
- foreach (Run r in cRuns)
- unerlines.Add(new LineFColor(
- r.Left, r.Top + r.BaseLine + underline, r.Left + r.Width, size * 0.1f * fixScale,
- r.Style.Color));
- }
- }
- #endregion Internal Methods
- #region Private Methods
- private float CalcWidth()
- {
- float width = 0;
- foreach (Word w in Words)
- foreach (Run r in w.Runs)
- width += r.Width;
- Word lastWord = Words.Count > 0 ? Words[Words.Count - 1] : null;
- if (lastWord != null && lastWord.Type == WordType.WhiteSpace)
- {
- foreach (Run r in lastWord.Runs)
- width -= r.Width;
- }
- return width;
- }
- #endregion Private Methods
- }
- public class Paragraph
- {
- #region Private Fields
- private List<Line> lines;
- private HtmlTextRenderer renderer;
- #endregion Private Fields
- #region Public Properties
- public List<Line> Lines
- {
- get { return lines; }
- }
- public HtmlTextRenderer Renderer
- {
- get { return renderer; }
- }
- #endregion Public Properties
- #region Public Constructors
- public Paragraph(HtmlTextRenderer renderer)
- {
- lines = new List<Line>();
- this.renderer = renderer;
- }
- #endregion Public Constructors
- #region Public Methods
- public override string ToString()
- {
- if (Lines.Count == 0) return "Lines[0]";
- StringBuilder sb = new StringBuilder();
- sb.AppendFormat("Lines[{0}]", Lines.Count);
- sb.Append("{");
- foreach (Line line in Lines)
- {
- sb.Append(line);
- sb.Append(",");
- }
- sb.Append("}");
- return sb.ToString();
- }
- #endregion Public Methods
- #region Internal Methods
- internal void AlignLines(bool forceJustify)
- {
- for (int i = 0; i < Lines.Count; i++)
- {
- HorzAlign align = Renderer.horzAlign;
- if (align == HorzAlign.Justify && i == Lines.Count - 1 && !forceJustify)
- align = HorzAlign.Left;
- Lines[i].AlignWords(align);
- }
- }
- #endregion Internal Methods
- }
- public abstract class Run
- {
- #region Protected Fields
- protected float baseLine;
- protected int charIndex;
- protected float descent;
- protected float height;
- protected float left;
- protected HtmlTextRenderer renderer;
- protected StyleDescriptor style;
- protected float top;
- //protected float FUnderline;
- //protected float FUnderlineSize;
- protected float width;
- protected Word word;
- #endregion Protected Fields
- #region Public Properties
- public float BaseLine
- {
- get { return baseLine; }
- set { baseLine = value; }
- }
- public int CharIndex
- {
- get { return charIndex; }
- }
- public float Descent
- {
- get { return descent; }
- set { descent = value; }
- }
- public float Height
- {
- get { return height; }
- set { height = value; }
- }
- public float Left
- {
- get { return left; }
- set { left = value; }
- }
- public HtmlTextRenderer Renderer
- {
- get { return renderer; }
- }
- public StyleDescriptor Style
- {
- get { return style; }
- }
- public float Top
- {
- get { return top; }
- set { top = value; }
- }
- //public float Underline
- //{
- // get { return FUnderline; }
- // set { FUnderline = value; }
- //}
- //public float UnderlineSize
- //{
- // get { return FUnderlineSize; }
- // set { FUnderlineSize = value; }
- //}
- public float Width
- {
- get { return width; }
- set { width = value; }
- }
- public Word Word
- {
- get { return word; }
- set { word = value; }
- }
- #endregion Public Properties
- #region Public Constructors
- public Run(HtmlTextRenderer renderer, Word word, StyleDescriptor style, float left, int charIndex)
- {
- this.renderer = renderer;
- this.word = word;
- this.style = style;
- this.left = left;
- this.charIndex = charIndex;
- }
- #endregion Public Constructors
- //public virtual void DrawBack(float top, float height)
- //{
- // if (FStyle.BackgroundColor.A > 0)
- // {
- // using (Brush brush = GetBackgroundBrush())
- // FRenderer.FGraphics.FillRectangle(brush, Left, top, Width, height);
- // }
- //}
- #region Public Methods
- public abstract void Draw();
- public abstract Run Split(float availableWidth, out Run secondPart);
- #endregion Public Methods
- #region Protected Methods
- protected Brush GetBackgroundBrush()
- {
- return new SolidBrush(style.BackgroundColor);
- }
- #endregion Protected Methods
- //public virtual void Draw(bool drawContents)
- //{
- // if ((FStyle.FontStyle & FontStyle.Underline) == FontStyle.Underline)
- // {
- // if (!FRenderer.FUnderLines)
- // {
- // float top = Top + FUnderline;
- // using (Pen pen = new Pen(FStyle.Color, FUnderlineSize * 0.1f))
- // if (FRenderer.FRightToLeft)
- // FRenderer.FGraphics.DrawLine(pen, Left - Width, top, Left, top);
- // else
- // FRenderer.FGraphics.DrawLine(pen, Left, top, Left + Width, top);
- // }
- // }
- // if ((FStyle.FontStyle & FontStyle.Strikeout) == FontStyle.Strikeout)
- // {
- // float top = Top + FBaseLine / 3 * 2;
- // using (Pen pen = new Pen(FStyle.Color, FStyle.Size * 0.1f))
- // if (FRenderer.FRightToLeft)
- // FRenderer.FGraphics.DrawLine(pen, Left - Width, top, Left, top);
- // else
- // FRenderer.FGraphics.DrawLine(pen, Left, top, Left + Width, top);
- // }
- //}
- }
- public class RunImage : Run
- {
- #region Private Fields
- private Image image;
- private string src;
- #endregion Private Fields
- #region Public Properties
- public Image Image { get { return image; } }
- #endregion Public Properties
- #region Public Constructors
- public RunImage(HtmlTextRenderer renderer, Word word, string src, StyleDescriptor style, float left, int charIndex, float img_width, float img_height) : base(renderer, word, style, left, charIndex)
- {
- base.style = new StyleDescriptor(style);
- this.src = src;
- //disable for exports because img tag not support strikeouts and underlines
- base.style.FontStyle &= ~(FontStyle.Strikeout | FontStyle.Underline);
- image = InlineImageCache.Load(Renderer.cache, src);
- Width = image.Width * Renderer.Scale;
- Height = image.Height * Renderer.Scale;
- if (img_height > 0)
- {
- if (img_width > 0)
- {
- Width = img_width * Renderer.Scale;
- Height = img_height * Renderer.Scale;
- }
- else
- {
- Width *= img_height / image.Height;
- Height = img_height * Renderer.Scale;
- }
- }
- else if (img_width > 0)
- {
- Width = img_width * Renderer.Scale;
- Height *= img_width / image.Width;
- }
- baseLine = Height;
- using (Font ff = style.GetFont())
- {
- float height = ff.GetHeight(Renderer.graphics.Graphics);
- float lineSpace = ff.FontFamily.GetLineSpacing(style.FontStyle);
- float descent = ff.FontFamily.GetCellDescent(style.FontStyle);
- base.descent = height * descent / lineSpace;
- }
- }
- #endregion Public Constructors
- #region Public Methods
- public override void Draw()
- //public override void Draw(bool drawContents)
- {
- //if (drawContents)
- if (image != null)
- {
- if (renderer.rightToLeft)
- renderer.graphics.DrawImage(image, new RectangleF(Left - Width, Top, Width, Height));
- else
- renderer.graphics.DrawImage(image, new RectangleF(Left, Top, Width, Height));
- }
- //base.Draw(drawContents);
- }
- public override Run Split(float availableWidth, out Run secondPart)
- {
- secondPart = this;
- return null;
- }
- #endregion Public Methods
- #region Internal Methods
- public Bitmap GetBitmap(out float width, out float height)
- {
- width = 1;
- height = 1;
- if (image == null)
- return new Bitmap(1, 1);
- width = image.Width;
- height = image.Height;
- float x = 0;
- float y = 0;
- float scaleX = width / this.Width;
- float scaleY = height / this.Height;
- if (left < renderer.displayRect.Left)
- {
- x = -((renderer.displayRect.Left - left) * scaleX);
- width += x;
- }
- if (top < renderer.displayRect.Top)
- {
- y = -((renderer.displayRect.Top - top) * scaleY);
- height += y;
- }
- if (left + base.width > renderer.displayRect.Right)
- {
- width -= ((left + base.width - renderer.displayRect.Right) * scaleX);
- }
- if (top + base.height > renderer.displayRect.Bottom)
- {
- height -= ((top + base.height - renderer.displayRect.Bottom) * scaleY);
- }
- if (width < 1) width = 1;
- if (height < 1) height = 1;
- Bitmap bmp = new Bitmap((int)width, (int)height);
- using (Graphics g = Graphics.FromImage(bmp))
- g.DrawImage(image, new PointF(x, y));
- width /= scaleX;
- height /= scaleY;
- return bmp;
- }
- #endregion Internal Methods
- //public override void ToHtml(FastString sb, bool download)
- //{
- // if(download)
- // {
- // if(FImage!=null)
- // {
- // using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
- // {
- // try
- // {
- // using (Bitmap bmp = new Bitmap(FImage.Width, FImage.Height))
- // {
- // using (Graphics g = Graphics.FromImage(bmp))
- // {
- // g.DrawImage(FImage, Point.Empty);
- // }
- // bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
- // }
- // ms.Flush();
- // sb.Append("<img src=\"data:image/png;base64,").Append(Convert.ToBase64String(ms.ToArray()))
- // .Append("\" width=\"").Append(FWidth.ToString(CultureInfo)).Append("\" height=\"").Append(FHeight.ToString(CultureInfo)).Append("\"/>");
- // }
- // catch(Exception e)
- // {
- // }
- // }
- // }
- // }else if(!String.IsNullOrEmpty(FSrc))
- // {
- // if (FImage != null)
- // {
- // sb.Append("<img src=\"").Append(FSrc).Append("\" width=\"").Append(FWidth.ToString(CultureInfo)).Append("\" height=\"").Append(FHeight.ToString(CultureInfo)).Append("\"/>");
- // }
- // else
- // {
- // sb.Append("<img src=\"").Append(FSrc).Append("\"/>");
- // }
- // }
- //}
- }
- public class RunText : Run
- {
- #region Private Fields
- private List<CharWithIndex> chars;
- private string text;
- #endregion Private Fields
- #region Public Properties
- public string Text { get { return text; } }
- #endregion Public Properties
- #region Public Constructors
- public RunText(HtmlTextRenderer renderer, Word word, StyleDescriptor style, List<CharWithIndex> text, float left, int charIndex) : base(renderer, word, style, left, charIndex)
- {
- using (Font ff = style.GetFont())
- {
- chars = new List<CharWithIndex>(text);
- this.text = GetString(text);
- if (ff.FontFamily.Name == "Wingdings" || ff.FontFamily.Name == "Webdings")
- {
- this.text = WingdingsToUnicodeConverter.Convert(this.text);
- }
- if (this.text.Length > 0)
- {
- base.charIndex = text[0].Index;
- if (word.Type == WordType.WhiteSpace)
- {
- //using (Font f = new Font("Consolas", 10))
- width = CalcSpaceWidth(this.text, ff);
- }
- else
- {
- width = Renderer.graphics.MeasureString(this.text, ff, int.MaxValue, base.renderer.format).Width;
- }
- }
- height = ff.GetHeight(Renderer.graphics.Graphics);
- float lineSpace = ff.FontFamily.GetLineSpacing(style.FontStyle);
- float ascent = ff.FontFamily.GetCellAscent(style.FontStyle);
- baseLine = height * ascent / lineSpace;
- descent = height - baseLine;
- if (style.BaseLine == HtmlTextRenderer.BaseLine.Subscript)
- descent += height * 0.45f;
- }
- }
- #endregion Public Constructors
- #region Public Methods
- public float CalcSpaceWidth(string text, Font ff)
- {
- return Renderer.graphics.MeasureString("1" + text + "2", ff, int.MaxValue, renderer.format).Width
- - Renderer.graphics.MeasureString("12", ff, int.MaxValue, renderer.format).Width;
- }
- public override void Draw()
- //public override void Draw(bool drawContents)
- {
- using (Font font = style.GetFont())
- using (Brush brush = GetBrush())
- {
- //if (drawContents)
- //{
- //#if DEBUG
- // SizeF size = FRenderer.FGraphics.MeasureString(FText, font, int.MaxValue, FRenderer.FFormat);
- // if (FRenderer.RightToLeft)
- // FRenderer.FGraphics.DrawRectangle(Pens.Red, Left - size.Width, Top, size.Width, size.Height);
- // else
- // FRenderer.FGraphics.DrawRectangle(Pens.Red, Left, Top, size.Width, size.Height);
- //#endif
- renderer.graphics.DrawString(text, font, brush, Left, Top, renderer.format);
- }
- //}
- //base.Draw(drawContents);
- }
- public Brush GetBrush()
- {
- return new SolidBrush(Style.Color);
- }
- public override Run Split(float availableWidth, out Run secondPart)
- {
- int size = chars.Count;
- if (size == 0)
- {
- secondPart = this;
- return null;
- }
- int from = 0;
- int point = size / 2;
- int to = size;
- Run r = null;
- while (to - from > 1)
- {
- List<CharWithIndex> list = new List<CharWithIndex>();
- for (int i = 0; i < point; i++)
- list.Add(chars[i]);
- r = new RunText(renderer, word, style, list, left, charIndex);
- if (r.Width > availableWidth)
- {
- if(point == 1)
- {
- // Single char width is less than availableWidth
- // Give up splitting
- secondPart = null;
- return this;
- }
- to = point;
- point = (to + from) / 2;
- }
- else
- {
- from = point;
- point = (to + from) / 2;
- }
- }
- if (to < 2)
- {
- secondPart = this;
- return null;
- }
- else
- {
- List<CharWithIndex> list = new List<CharWithIndex>();
- for (int i = point; i < size; i++)
- list.Add(chars[i]);
- secondPart = new RunText(renderer, word, style, list, left + r.Width, charIndex);
- list.Clear();
- for (int i = 0; i < point; i++)
- list.Add(chars[i]);
- r = new RunText(renderer, word, style, list, left, charIndex);
- return r;
- }
- }
- #endregion Public Methods
- #region Private Methods
- private string GetString(List<CharWithIndex> str)
- {
- renderer.cacheString.Clear();
- foreach (CharWithIndex ch in str)
- {
- renderer.cacheString.Append(ch.Char);
- }
- return renderer.cacheString.ToString();
- }
- #endregion Private Methods
- //public override void ToHtml(FastString sb, bool download)
- //{
- // //if (FWord.Type == WordType.Tab)
- // // sb.Append("<span style=\"display:inline-block;min-width:").Append((FWidth * 0.99f).ToString(CultureInfo)).Append("px;\">");
- // foreach(char ch in Text)
- // {
- // switch (ch)
- // {
- // case '"':
- // sb.Append(""");
- // break;
- // case '&':
- // sb.Append("&");
- // break;
- // case '<':
- // sb.Append("<");
- // break;
- // case '>':
- // sb.Append(">");
- // break;
- // case '\t':
- // sb.Append("	");
- // break;
- // default:
- // sb.Append(ch);
- // break;
- // }
- // }
- // //if (FWord.Type == WordType.Tab)
- // // sb.Append("</span>");
- //}
- }
- public class Word
- {
- #region Private Fields
- private float baseLine;
- private float descent;
- private float height;
- private Line line;
- private HtmlTextRenderer renderer;
- private List<Run> runs;
- private WordType type;
- #endregion Private Fields
- #region Public Properties
- public float BaseLine { get { return baseLine; } }
- public float Descent { get { return descent; } }
- public float Height { get { return height; } }
- public Line Line
- {
- get { return line; }
- set { line = value; }
- }
- public HtmlTextRenderer Renderer
- {
- get { return renderer; }
- }
- public List<Run> Runs
- {
- get { return runs; }
- }
- public WordType Type
- {
- get { return type; }
- set { type = value; }
- }
- #endregion Public Properties
- #region Public Constructors
- public Word(HtmlTextRenderer renderer, Line line)
- {
- this.renderer = renderer;
- runs = new List<Run>();
- this.line = line;
- }
- public Word(HtmlTextRenderer renderer, Line line, WordType type)
- {
- this.renderer = renderer;
- runs = new List<Run>();
- this.line = line;
- this.type = type;
- }
- #endregion Public Constructors
- #region Internal Methods
- internal void CalcMetrics()
- {
- baseLine = 0;
- descent = 0;
- foreach (Run run in Runs)
- {
- baseLine = Math.Max(baseLine, run.BaseLine);
- descent = Math.Max(descent, run.Descent);
- }
- height = baseLine + descent;
- }
- #endregion Internal Methods
- }
- #endregion Public Classes
- #region Internal Classes
- internal class SimpleFastReportHtmlElement
- {
- #region Public Fields
- public Dictionary<string, string> attributes;
- public bool isSelfClosed;
- public bool isEnd;
- public string name;
- #endregion Public Fields
- #region Private Fields
- private Dictionary<string, string> style;
- #endregion Private Fields
- #region Public Properties
- public bool IsSelfClosed
- {
- get
- {
- switch (name)
- {
- case "img":
- case "br":
- return true;
- default:
- return isSelfClosed;
- }
- }
- set { isSelfClosed = value; }
- }
- /// <summary>
- /// Be care generates dictionary only one time
- /// </summary>
- public Dictionary<string, string> Style
- {
- get
- {
- if (style == null && attributes != null && attributes.ContainsKey("style"))
- {
- string styleString = attributes["style"];
- style = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- foreach (string kv in styleString.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
- {
- string[] strs = kv.Split(':');
- if (strs.Length == 2)
- {
- style[strs[0]] = strs[1];
- }
- }
- }
- return style;
- }
- }
- #endregion Public Properties
- #region Public Constructors
- public SimpleFastReportHtmlElement(string name)
- {
- this.name = name;
- }
- public SimpleFastReportHtmlElement(string name, Dictionary<string, string> attributes)
- {
- this.name = name;
- this.attributes = attributes;
- }
- public SimpleFastReportHtmlElement(string name, bool isEnd)
- {
- this.name = name;
- this.isEnd = isEnd;
- }
- public SimpleFastReportHtmlElement(string name, bool isBegin, Dictionary<string, string> attributes)
- {
- this.name = name;
- this.isEnd = isBegin;
- this.attributes = attributes;
- }
- public SimpleFastReportHtmlElement(string name, bool isBegin, bool isSelfClosed)
- {
- this.name = name;
- this.isEnd = isBegin;
- this.IsSelfClosed = isSelfClosed;
- }
- public SimpleFastReportHtmlElement(string name, bool isBegin, bool isSelfClosed, Dictionary<string, string> attributes)
- {
- this.name = name;
- this.isEnd = isBegin;
- this.IsSelfClosed = isSelfClosed;
- this.attributes = attributes;
- }
- #endregion Public Constructors
- #region Public Methods
- public override string ToString()
- {
- StringBuilder sb = new StringBuilder();
- sb.Append("<");
- if (isEnd)
- sb.Append("/");
- sb.Append(name);
- if (attributes != null)
- {
- foreach (KeyValuePair<string, string> kv in attributes)
- {
- sb.Append(" ");
- sb.Append(kv.Key);
- sb.Append("=\"");
- sb.Append(kv.Value);
- sb.Append("\"");
- }
- }
- if (IsSelfClosed)
- sb.Append('/');
- sb.Append(">");
- return sb.ToString();
- }
- #endregion Public Methods
- }
- internal class SimpleFastReportHtmlReader
- {
- #region Private Fields
- private CharWithIndex @char;
- private SimpleFastReportHtmlElement element;
- private int lastPosition;
- private int position;
- private string substring;
- private string text;
- #endregion Private Fields
- #region Public Properties
- public CharWithIndex Character
- {
- get
- {
- return @char;
- }
- }
- public SimpleFastReportHtmlElement Element
- {
- get
- {
- return element;
- }
- }
- public bool IsEOF
- {
- get
- {
- return position >= text.Length;
- }
- }
- public bool IsNotEOF
- {
- get
- {
- return position < text.Length;
- }
- }
- public int LastPosition
- {
- get { return lastPosition; }
- }
- public int Position
- {
- get
- {
- return position;
- }
- set
- {
- position = value;
- }
- }
- #endregion Public Properties
- #region Public Constructors
- public SimpleFastReportHtmlReader(string text)
- {
- this.text = text;
- }
- #endregion Public Constructors
- #region Public Methods
- public static bool IsCanBeCharacterInTagName(char c)
- {
- if (c == ':') return true;
- if ('A' <= c && c <= 'Z') return true;
- if (c == '_') return true;
- if ('a' <= c && c <= 'z') return true;
- if (c == '-') return true;//
- if (c == '.') return true;//
- if ('0' <= c && c <= '9') return true;//
- if (c == '\u00B7') return true;//
- if ('\u00C0' <= c && c <= '\u00D6') return true;
- if ('\u00D8' <= c && c <= '\u00F6') return true;
- if ('\u00F8' <= c && c <= '\u02FF') return true;
- if ('\u0300' <= c && c <= '\u036F') return true;//
- if ('\u0370' <= c && c <= '\u037D') return true;
- if ('\u037F' <= c && c <= '\u1FFF') return true;
- if ('\u200C' <= c && c <= '\u200D') return true;
- if ('\u203F' <= c && c <= '\u2040') return true;//
- if ('\u2070' <= c && c <= '\u218F') return true;
- if ('\u2C00' <= c && c <= '\u2FEF') return true;
- if ('\u3001' <= c && c <= '\uD7FF') return true;
- if ('\uF900' <= c && c <= '\uFDCF') return true;
- if ('\uFDF0' <= c && c <= '\uFFFD') return true;
- return false;
- }
- public static bool IsCanBeFirstCharacterInTagName(char c)
- {
- if (c == ':') return true;
- if ('A' <= c && c <= 'Z') return true;
- if (c == '_') return true;
- if ('a' <= c && c <= 'z') return true;
- if ('\u00C0' <= c && c <= '\u00D6') return true;
- if ('\u00D8' <= c && c <= '\u00F6') return true;
- if ('\u00F8' <= c && c <= '\u02FF') return true;
- if ('\u0370' <= c && c <= '\u037D') return true;
- if ('\u037F' <= c && c <= '\u1FFF') return true;
- if ('\u200C' <= c && c <= '\u200D') return true;
- if ('\u2070' <= c && c <= '\u218F') return true;
- if ('\u2C00' <= c && c <= '\u2FEF') return true;
- if ('\u3001' <= c && c <= '\uD7FF') return true;
- if ('\uF900' <= c && c <= '\uFDCF') return true;
- if ('\uFDF0' <= c && c <= '\uFFFD') return true;
- return false;
- }
- /// <summary>
- /// Return true if read char
- /// </summary>
- /// <returns></returns>
- public bool Read()
- {
- lastPosition = position;
- switch ((@char = new CharWithIndex(text[position], position)).Char)
- {
- case '&':
- if (Converter.FromHtmlEntities(text, ref position, out substring))
- @char.Char = substring[0];
- position++;
- return true;
- case '<':
- element = GetElement(text, ref position);
- position++;
- if (element != null)
- switch (element.name)
- {
- case "br":
- @char = new CharWithIndex('\n', lastPosition);
- return true;
- default:
- return false;
- }
- return true;
- }
- position++;
- return true;
- }
- #endregion Public Methods
- #region Private Methods
- private SimpleFastReportHtmlElement GetElement(string line, ref int index)
- {
- int to = line.Length - 1;
- int i = index + 1;
- bool closed = false;
- if (i <= to)
- if (closed = line[i] == '/')
- i++;
- if (i <= to)
- if (!IsCanBeFirstCharacterInTagName(line[i]))
- return null;
- for (i++; i <= to && line[i] != ' ' && line[i] != '>' && line[i] != '/'; i++)
- {
- if (!IsCanBeCharacterInTagName(line[i]))
- return null;
- }
- if (i <= to)
- {
- string tagName = line.Substring(index + (closed ? 2 : 1), i - index - (closed ? 2 : 1));
- Dictionary<string, string> attrs = null;
- if (!IsAvailableTagName(tagName))
- return null;
- if (line[i] == ' ')
- {
- //read attributes
- for (; i <= to && line[i] != '>' && line[i] != '/'; i++)
- {
- for (; i <= to && line[i] == ' '; i++) ;
- if (line[i] == '>' || line[i] == '/') i--;
- else
- {
- if (!IsCanBeFirstCharacterInTagName(line[i]))
- return null;
- int attrNameStartIndex = i;
- for (i++; i <= to && line[i] != '='; i++)
- if (!IsCanBeFirstCharacterInTagName(line[i]))
- return null;
- int attrNameEndIndex = i; //index of =
- i++;
- if (i <= to && line[i] == '"')
- {//begin attr
- int attrValueStartIndex = i + 1;
- for (i++; i <= to && line[i] != '"'; i++)
- {
- switch (line[i])
- {
- case '<': return null;
- case '>': return null;
- }
- }
- if (i <= to)
- {
- string attrName = line.Substring(attrNameStartIndex, attrNameEndIndex - attrNameStartIndex);
- string attrValue = line.Substring(attrValueStartIndex, i - attrValueStartIndex);
- if (attrs == null) attrs = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- attrs[attrName] = attrValue;
- }
- }
- }
- }
- }
- if (i <= to)
- {
- if (line[i] == '>')
- {
- index = i;
- return new SimpleFastReportHtmlElement(tagName, closed, false, attrs);
- }
- if (line[i] == '/' && i < to && line[i + 1] == '>')
- {
- index = i + 1;
- return new SimpleFastReportHtmlElement(tagName, closed, true, attrs);
- }
- }
- }
- return null;
- }
- private bool IsAvailableTagName(string tagName)
- {
- switch (tagName)
- {
- case "b":
- case "br":
- case "i":
- case "u":
- case "sub":
- case "sup":
- case "img":
- //case "font":
- case "strike":
- case "span":
- return true;
- }
- return false;
- }
- #endregion Private Methods
- }
- /// <summary>
- /// Represents a style used in HtmlTags mode. Color does not affect the equals function.
- /// </summary>
- public class StyleDescriptor
- {
- #region Private Fields
- private static readonly Color DefaultColor = Color.Transparent;
- private Color backgroundColor;
- private BaseLine baseLine;
- private Color color;
- private FontFamily font;
- private FontStyle fontStyle;
- private float size;
- #endregion Private Fields
- #region Public Properties
- public Color BackgroundColor
- {
- get { return backgroundColor; }
- set { backgroundColor = value; }
- }
- public BaseLine BaseLine
- {
- get { return baseLine; }
- set { baseLine = value; }
- }
- public Color Color
- {
- get { return color; }
- set { color = value; }
- }
- public FontFamily Font
- {
- get { return font; }
- set { font = value; }
- }
- public FontStyle FontStyle
- {
- get { return fontStyle; }
- set { fontStyle = value; }
- }
- public float Size
- {
- get { return size; }
- set { size = value; }
- }
- #endregion Public Properties
- #region Public Constructors
- public StyleDescriptor(FontStyle fontStyle, Color color, BaseLine baseLine, FontFamily font, float size)
- {
- this.fontStyle = fontStyle;
- this.color = color;
- this.baseLine = baseLine;
- this.font = font;
- this.size = size;
- backgroundColor = DefaultColor;
- }
- public StyleDescriptor(StyleDescriptor styleDescriptor)
- {
- fontStyle = styleDescriptor.fontStyle;
- color = styleDescriptor.color;
- baseLine = styleDescriptor.baseLine;
- font = styleDescriptor.font;
- size = styleDescriptor.size;
- backgroundColor = styleDescriptor.backgroundColor;
- }
- #endregion Public Constructors
- #region Public Methods
- public override bool Equals(object obj)
- {
- StyleDescriptor descriptor = obj as StyleDescriptor;
- return descriptor != null &&
- baseLine == descriptor.baseLine &&
- font == descriptor.font &&
- fontStyle == descriptor.fontStyle &&
- size == descriptor.size;
- }
- /// <summary>
- /// returns true if objects realy equals
- /// </summary>
- /// <param name="obj"></param>
- /// <returns></returns>
- public bool FullEquals(StyleDescriptor obj)
- {
- return obj != null && GetHashCode() == obj.GetHashCode() &&
- this.Equals(obj) &&
- color.Equals(obj.color) &&
- backgroundColor.Equals(obj.backgroundColor);
- }
- public Font GetFont()
- {
- float fontSize = size;
- if (baseLine != BaseLine.Normal)
- fontSize *= 0.6f;
- FontStyle fontStyle = FontStyle;
- fontStyle = fontStyle & ~FontStyle.Underline & ~FontStyle.Strikeout;
- return new Font(font, fontSize, fontStyle);
- }
- public override int GetHashCode()
- {
- int hashCode = -1631016721;
- unchecked
- {
- hashCode = hashCode * -1521134295 + baseLine.GetHashCode();
- hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(font.Name);
- hashCode = hashCode * -1521134295 + fontStyle.GetHashCode();
- hashCode = hashCode * -1521134295 + size.GetHashCode();
- }
- return hashCode;
- }
- public void ToHtml(FastString sb, bool close)
- {
- float fontsize = size / DrawUtils.ScreenDpiFX;
- if (close)
- {
- sb.Append("</span>");
- if ((fontStyle & FontStyle.Strikeout) == FontStyle.Strikeout) sb.Append("</strike>");
- if ((fontStyle & FontStyle.Underline) == FontStyle.Underline) sb.Append("</u>");
- if ((fontStyle & FontStyle.Italic) == FontStyle.Italic) sb.Append("</i>");
- if ((fontStyle & FontStyle.Bold) == FontStyle.Bold) sb.Append("</b>");
- switch (baseLine)
- {
- case BaseLine.Subscript: sb.Append("</sub>"); break;
- case BaseLine.Superscript: sb.Append("</sup>"); break;
- }
- }
- else
- {
- switch (baseLine)
- {
- case BaseLine.Subscript: sb.Append("<sub>"); break;
- case BaseLine.Superscript: sb.Append("<sup>"); break;
- }
- if ((fontStyle & FontStyle.Bold) == FontStyle.Bold) sb.Append("<b>");
- if ((fontStyle & FontStyle.Italic) == FontStyle.Italic) sb.Append("<i>");
- if ((fontStyle & FontStyle.Underline) == FontStyle.Underline) sb.Append("<u>");
- if ((fontStyle & FontStyle.Strikeout) == FontStyle.Strikeout) sb.Append("<strike>");
- sb.Append("<span style=\"");
- if (backgroundColor.A > 0) sb.Append(String.Format(CultureInfo, "background-color:rgba({0},{1},{2},{3});", backgroundColor.R, backgroundColor.G, backgroundColor.B, ((float)backgroundColor.A) / 255f));
- if (color.A > 0) sb.Append(String.Format(CultureInfo, "color:rgba({0},{1},{2},{3});", color.R, color.G, color.B, ((float)color.A) / 255f));
- if (font != null) { sb.Append("font-family:"); sb.Append(font.Name); sb.Append(";"); }
- if (fontsize > 0) { sb.Append("font-size:"); sb.Append(fontsize.ToString(CultureInfo)); sb.Append("pt;"); }
- sb.Append("\">");
- }
- }
- #endregion Public Methods
- }
- private class OwnHashSet<T>
- {
- #if DOTNET_4
- private HashSet<T> internalHashSet;
- public int Count { get { return internalHashSet.Count; } }
- #else
- private Dictionary<T, object> internalDictionary;
- private object FHashSetObject;
- public int Count { get { return internalDictionary.Count; } }
- #endif
- public OwnHashSet()
- {
- #if DOTNET_4
- internalHashSet = new HashSet<T>();
- #else
- internalDictionary = new Dictionary<T, object>();
- FHashSetObject = new object();
- #endif
- }
- public void Clear()
- {
- #if DOTNET_4
- internalHashSet.Clear();
- #else
- internalDictionary.Clear();
- #endif
- }
- public bool Contains(T value)
- {
- #if DOTNET_4
- return internalHashSet.Contains(value);
- #else
- return internalDictionary.ContainsKey(value);
- #endif
- }
- public void Add(T value)
- {
- #if DOTNET_4
- internalHashSet.Add(value);
- #else
- internalDictionary.Add(value, FHashSetObject);
- #endif
- }
- }
- #endregion Internal Classes
- #region IDisposable Support
- private bool disposedValue = false;
- protected virtual void Dispose(bool disposing)
- {
- if (!disposedValue)
- {
- if (disposing)
- {
- format.Dispose();
- format = null;
- }
- disposedValue = true;
- }
- }
- public void Dispose()
- {
- Dispose(true);
- }
- #endregion IDisposable Support
- }
- /// <summary>
- /// Class that converts strings with Wingdings characters to Unicode strings.
- /// </summary>
- public static class WingdingsToUnicodeConverter
- {
- /// <summary>
- /// Converts string with Wingdings characters to its Unicode analog.
- /// </summary>
- /// <param name="str">The string that should be converted.</param>
- /// <returns></returns>
- public static string Convert(string str)
- {
- char[] chars = str.ToCharArray();
- for (int i = 0; i < chars.Length; i++)
- {
- if (chars[i] >= 0x20 && chars[i] <= 0xFF)
- {
- chars[i] = (char)(0xF000 + chars[i]);
- }
- }
- return new string(chars);
- }
- }
- }
|