Lazarus

Programming => Packages and Libraries => RichMemo => Topic started by: rick2691 on March 25, 2017, 03:58:15 pm

Title: Paragraph Formatting and Syriac fonts
Post by: rick2691 on March 25, 2017, 03:58:15 pm
skalogryz,

I am still having trouble with Syriac vowels that are printed too high, as in the case of a very tall consonant. With them the vowel is set above the consonant. The result is that the upper portion of the vowel is clipped off.

If I change the line height by paragraph formatting, it has no change for the problem because it applies all of the height (actually a descent) to the bottom of the line base.

If I increase the top space for the entire paragraph it resolves the problem for the first line of the paragraph. But the problem remains for the rest of the lines within the paragraph.

My question is ... Can a parameter to formatting (TParaMetric) be added that will increase the space above a line base height, so that all of the paragraph lines will have a height above it? Perhaps it could be called "LinesHeader", and be a points value (as opposed to LineSpacing that has a multiplier).

I suspect that this additional parameter might even help to suppress Font Binding.

Rick
Title: Re: Paragraph Formatting and Syriac fonts
Post by: skalogryz on March 26, 2017, 01:45:13 am
Unfortunately, LineSpacing is the only option. There no APIs exposed to control the characters layout.

What you could try is inserting a character of a different height into the line.
That would cause the line to get some extra height.

It's a hacky way of doing things, but it is something to try.
Title: Re: Paragraph Formatting and Syriac fonts
Post by: rick2691 on March 28, 2017, 03:56:42 pm
I can see that it would work, but you are correct, it would be both unsightly and impractical. Is this another limitation of the RichEdit driver? I have an editor that has a function for a LineHeader, and it operates throughout a paragraph.

Rick
Title: Re: Paragraph Formatting and Syriac fonts
Post by: skalogryz on March 28, 2017, 04:41:40 pm
I can see that it would work, but you are correct, it would be both unsightly and impractical. Is this another limitation of the RichEdit driver?
Yes. There's no LineHeader parameter available for RichEdit.
You can review all available options in PARAFORMAT2 (https://msdn.microsoft.com/en-us/library/windows/desktop/bb787942(v=vs.85).aspx) structure description.
The only thing that controls the spacing between lines is LineSpacing.

I have an editor that has a function for a LineHeader, and it operates throughout a paragraph.
what name?
I'd think that any application that's designed to text processing, would not be based on the RichEdit, due to its limitations.

...and thus I'd consider investing time/efforts/money into KMemo, or any other not RichEdit based solution.
Title: Re: Paragraph Formatting and Syriac fonts
Post by: skalogryz on March 28, 2017, 05:08:47 pm
hmm... dyLineSpacing and bLineSpacingRule...

RichMemo is forcing LineSpacing to use style 5 - and thus the extact line spacing. (this has to happen for cross-platform compatibility)

Now, I'm thinking what if it's causing the issue for you.
Title: Re: Paragraph Formatting and Syriac fonts
Post by: rick2691 on March 28, 2017, 10:18:43 pm
skalogryz,

I set all numerals to superscript which elevated the base height. It works when a verse number is in the line. Yet lines without are are still suffering from clipping.

Whatever you can do remedy the problem would be appreciated.

The editor is DavkaWriter. A PNG of their paragraph formatting is attached (noname.png). Their history is that they started with RTF, but now are proprietary. I don't know what they currently use, but it is still based on RTF modelling.

In the lower portion they have PTS before a line and after. It is separate from Paragraph before and after. Their WebSite is http://davka.com/index.php?route=product/product&product_id=67

I have used it for decades, and it is the best editor I have ever used. But they do not do Unicode, and have no provision for Syriac.

Rick

Title: Re: Paragraph Formatting and Syriac fonts
Post by: rick2691 on March 28, 2017, 10:31:08 pm
OK. I said I have used it for decades... my perception of their doing HEADER height was wrong. It is a HEADER to the paragraph. It has been decades since I worried about being able to do it. I only thought that they did it.

Sorry, Rick
Title: Re: Paragraph Formatting and Syriac fonts
Post by: rick2691 on March 29, 2017, 06:20:57 pm
OK again... I knew that I used to do this. It was with Polyedit. They are at http://polyedit.com

Attached is their format screen. It has a dialogue for lines that is called "At Least", and you enter a point height. It raises the base height. I used to use it for Syriac Unicode.
Title: Re: Paragraph Formatting and Syriac fonts
Post by: rick2691 on March 29, 2017, 07:18:11 pm
By the document you referenced it looks like bLineSpacingRule does what PolyEdit does...

bLineSpacingRule
Type: BYTE

Type of line spacing. To use this member, set the PFM_LINESPACING flag in the dwMask member. This member can be one of the following values.

Value Meaning

0 Single spacing. The dyLineSpacing member is ignored.

1 One-and-a-half spacing. The dyLineSpacing member is ignored.

2 Double spacing. The dyLineSpacing member is ignored.

3 The dyLineSpacing member specifies the spacing from one line to the next, in twips. However, if dyLineSpacing specifies a value that is less than single spacing, the control displays single-spaced text.

4 The dyLineSpacing member specifies the spacing from one line to the next, in twips. The control uses the exact spacing specified, even if dyLineSpacing specifies a value that is less than single spacing.

5 The value of dyLineSpacing / 20 is the spacing, in lines, from one line to the next. Thus, setting dyLineSpacing to 20 produces single-spaced text, 40 is double spaced, 60 is triple spaced, and so on.

==================================

I found an example of using it with C#... https://www.gemboxsoftware.com/document/examples/word-paragraph-formatting/602

It has the following for the AtLeast function...
                    ParagraphFormat = new ParagraphFormat
                    {
                        LeftIndentation = 25,
                        RightIndentation = 25,
                        SpecialIndentation = -25,
                        LineSpacing = 10,
                        LineSpacingRule = LineSpacingRule.AtLeast
                    }

==================================

Rick
Title: Re: Paragraph Formatting and Syriac fonts
Post by: rick2691 on March 29, 2017, 08:48:07 pm
Here is one that makes it easier. It is RichEdit and Pascal...

Code: Pascal  [Select]
  1. //set the paragraph line spacing in a TRichedit?
  2. //Title: set the paragraph line spacing in a TRichedit?
  3.  
  4. uses
  5.   RichEdit;
  6.  
  7. procedure RE_SetLineSpacing(ARichEdit: TRichEdit; lineSpacing: Byte);
  8. var
  9.   pf2: ParaFormat2;
  10. begin
  11.   FillChar(pf2, SizeOf(pf2), 0);
  12.   pf2.cbSize := SizeOf(PARAFORMAT2);
  13.   pf2.dwMask := PFM_LINESPACING;
  14.   pf2.bLineSpacingRule := lineSpacing;
  15.   SendMessage(ARichEdit.Handle, EM_SETPARAFORMAT, 0, Longint(@pf2));
  16. end;
  17.  
  18. //Example: Setlinespacing to 1:
  19. procedure TForm1.Button1Click(Sender: TObject);
  20. begin
  21.   RE_SetLineSpacing(RichEdit1, 1);
  22. end;
  23.  
Title: Re: Paragraph Formatting and Syriac fonts
Post by: skalogryz on March 29, 2017, 09:32:45 pm
Here is one that makes it easier. It is RichEdit and Pascal...
This is how you would use it for rich memo. :D

Code: Pascal  [Select]
  1. uses
  2.   RichMemo;
  3.  
  4. procedure RE_SetLineSpacing(ARichEdit: TRichMemo; lineSpacing: Byte);
  5. var
  6.   pf2: ParaFormat2;
  7. begin
  8.   FillChar(pf2, SizeOf(pf2), 0);
  9.   pf2.cbSize := SizeOf(PARAFORMAT2);
  10.   pf2.dwMask := PFM_LINESPACING;
  11.   pf2.bLineSpacingRule := lineSpacing;
  12.   SendMessage(ARichEdit.Handle, EM_SETPARAFORMAT, 0, Longint(@pf2));
  13. end;
  14.  
  15. //Example: Setlinespacing to 1:
  16. procedure TForm1.Button1Click(Sender: TObject);
  17. begin
  18.   RE_SetLineSpacing(RichMemo1, 1);
  19. end;
  20.  
Title: Re: Paragraph Formatting and Syriac fonts
Post by: rick2691 on March 29, 2017, 10:33:46 pm
Thanks. I'll see if it works. I'll use the AtLeast or Exact method instead of LineSpacing.

Rick
Title: Re: Paragraph Formatting and Syriac fonts
Post by: rick2691 on March 30, 2017, 08:03:14 pm
I was able to get it to work. It does both: Get and Set the parameter.

It also achieves my goal: The elevated Syriac vowels are not chopped.

Thanks for the help.

Rick
Title: Re: Paragraph Formatting and Syriac fonts
Post by: skalogryz on March 30, 2017, 09:03:06 pm
so, what settings did you use?
Title: Re: Paragraph Formatting and Syriac fonts
Post by: rick2691 on March 31, 2017, 01:56:39 pm
The following are the codes that I adapted...

Getting paragraph parameters:
Code: Pascal  [Select]
  1. procedure TCmdForm.MnuFormatClick(Sender: TObject);
  2. var m: TParaMetric;
  3.     s: string;
  4.     FParaProps: TParaFormat2;
  5.     Result: boolean;
  6.     z: extended;
  7. begin
  8.   PopTop:= PageControl1.top;
  9.   PopMid:= CmdForm.width div 2;
  10.   FormatOfs.top:= PopTop;
  11.   FormatOfs.left:= PopMid - (FormatOfs.width div 2);
  12.   if FormatOfs.left<0 then FormatOfs.left:= 0;
  13.  
  14.   // get paragraph offset metrics
  15.   PageMemo.GetParaMetric(PageMemo.SelStart, m);
  16.   OfsTop.Text:= IntToStr(round(m.SpaceBefore));
  17.   OfsInd.Text:= IntToStr(round(m.FirstLine));
  18.   OfsLft.Text:= IntToStr(round(m.HeadIndent));
  19.   OfsRht.Text:= IntToStr(round(m.TailIndent));
  20.   OfsBtm.Text:= IntToStr(round(m.SpaceAfter));
  21.   Str(m.LineSpacing:0:2,s); OfsLns.Text:= s;
  22.  
  23.   // get paragraph LTR/RTL flow
  24.   FillChar(FParaProps, SizeOf(TParaFormat2), 0);
  25.   FParaProps.cbSize:= SizeOf(TParaFormat2);
  26.   PageMemo.Perform(EM_GetParaFormat, 0, LParam(@FParaProps));
  27.   if (FParaProps.dwMask and PFM_RTLPARA)=0
  28.       then Result:= False
  29.       else Result:= (FParaProps.wEffects and PFE_RTLPARA)<>0;
  30.   if Result then btnRTL.checked:= true else btnLTR.checked:= true;
  31.  
  32.   // get paragraph line metrics
  33.   if (FParaProps.dwMask and PFM_LINESPACING)<>0 then
  34.      begin
  35.         LnsMultiply.checked:= true;
  36.         if (FParaProps.bLineSpacingRule=0) then // single
  37.            begin
  38.            LnsMult.caption:= 'Multiplier Factor';
  39.            LnsMultiply.checked:= true; OfsLns.Text:= '1.2';
  40.            end;
  41.         if (FParaProps.bLineSpacingRule=1) then // one&half
  42.            begin
  43.            LnsMult.caption:= 'Multiplier Factor';
  44.            LnsMultiply.checked:= true; OfsLns.Text:= '1.5';
  45.            end;
  46.         if (FParaProps.bLineSpacingRule=2) then // double
  47.            begin
  48.            LnsMult.caption:= 'Multiplier Factor';
  49.            LnsMultiply.checked:= true; OfsLns.Text:= '2.0';
  50.            end;
  51.  
  52.         if (FParaProps.bLineSpacingRule=3) then // minimum
  53.            begin
  54.            LnsMult.caption:= 'Minimum Points';
  55.            LnsAtLeast.checked:= true;
  56.            z:= FParaProps.dyLineSpacing/20;
  57.            Str(z:0:0,s); OfsLns.Text:= s;
  58.            end;
  59.         if (FParaProps.bLineSpacingRule=4) then // maximum
  60.            begin
  61.            LnsMult.caption:= 'Maximum Points';
  62.            LnsExactly.checked:= true;
  63.            z:= FParaProps.dyLineSpacing/20;
  64.            Str(z:0:0,s); OfsLns.Text:= s;
  65.            end;
  66.      end;
  67.  
  68.   FormatOfs.Visible:= true;
  69. end;
  70.  

Setting paragraph parameters:
Code: Pascal  [Select]
  1. procedure TCmdForm.SetOfsLns(Sender: TObject);  
  2. var
  3.   m: TParaMetric;
  4.   pf2: ParaFormat2;
  5.   x: double;
  6.   inpOK: boolean;
  7.   err: integer;
  8. begin
  9.   InitParaMetric(m);
  10.   Val(OfsLns.Text, x, Err);
  11.   InpOK:= (Err=0);
  12.   if InpOK then
  13.      begin
  14.      if (LnsMultiply.checked=true) then // descending multiplier
  15.         begin
  16.         m.LineSpacing:= x;
  17.         PageMemo.SetRangeParaParams(PageMemo.SelStart, PageMemo.SelLength, [pmm_LineSpacing], m);
  18.         end;
  19.      if (LnsAtLeast.checked=true) then  // ascending minimum
  20.         begin
  21.         FillChar(pf2, SizeOf(pf2), 0);
  22.         pf2.cbSize := SizeOf(PARAFORMAT2);
  23.         pf2.dyLineSpacing:= round(x*20);
  24.         pf2.dwMask := PFM_LINESPACING;
  25.         pf2.bLineSpacingRule := 3;
  26.         SendMessage(PageMemo.Handle, EM_SETPARAFORMAT, 0, Longint(@pf2));
  27.         end;
  28.      if (LnsExactly.checked=true) then // ascending maximum
  29.         begin
  30.         FillChar(pf2, SizeOf(pf2), 0);
  31.         pf2.cbSize := SizeOf(PARAFORMAT2);
  32.         pf2.dyLineSpacing:= round(x*20);
  33.         pf2.dwMask := PFM_LINESPACING;
  34.         pf2.bLineSpacingRule := 4;
  35.         SendMessage(PageMemo.Handle, EM_SETPARAFORMAT, 0, Longint(@pf2));
  36.         end;
  37.      end else if (length(OfsLns.Text)>0)
  38.                  then showmessage('Improper value entry.')
  39.                  else showmessage('Must enter value.')
  40. end;
  41.  

Attached is the form appearing with its altered text, which has an exaggerated Minimum Ascending Value of 20 points for demonstration.

Note:
1.  Multiplier is a descending mask from the top of the font ribbon.
2.  Minimum is an ascending mask from the bottom of the font ribbon.
     The font ribbon can be sized to greater than the minimum, and the
     minimum setting will be ignored.
3.  Maximum is an ascending mask from the bottom of the font ribbon.
     The font ribbon can be sized to greater than the maximum, but the
     ribbon will be clipped at the maximum setting.
4.  It is because the Minimum and Maximum settings are ascending that
     they assist with fonts that have high ascendance and elevated symbols.
Title: Re: Paragraph Formatting and Syriac fonts
Post by: rick2691 on April 01, 2017, 02:54:57 pm
I have encounted a problem with the previously posted code. I had not tested it with setting offsets for top, bottom, left, right, and first (while using the minimum and maximum methods). Using the Multiplier method has not shown any problem.

Since I have retained the SetRangeParaParams method for top, bottom, left, right, and first... It appears that they are including the LineSpacing (which is in points for minimum and maximum methods) as a multipiler.

I am going to try to remedy this by including those parameters with the bLineSpacingRule method, which allows those values to be included with it.

Rick
Title: Re: Paragraph Formatting and Syriac fonts
Post by: rick2691 on April 01, 2017, 06:40:33 pm
This is a revision. I revised everything to use the same method. It seems to operate well.

Code: Pascal  [Select]
  1. procedure TCmdForm.SetOfsTop(Sender: TObject);
  2. var
  3.   pf2: ParaFormat2;
  4.   x: double;
  5.   inpOK: boolean;
  6.   err: integer;
  7.  
  8. begin
  9.   Val(OfsTop.Text, x, Err);
  10.   InpOK:= (Err=0);
  11.   if InpOK then
  12.      begin
  13.      FillChar(pf2, SizeOf(pf2), 0);
  14.      pf2.cbSize := SizeOf(PARAFORMAT2);
  15.      SendMessage(PageMemo.Handle, EM_GetParaFormat, 0, LParam(@pf2));
  16.      pf2.dwMask := PFM_SPACEBEFORE;
  17.      pf2.dySpaceBefore:= round(x*20);
  18.      SendMessage(PageMemo.Handle, EM_SETPARAFORMAT, 0, Longint(@pf2));
  19.      end else if (length(OfsTop.Text)>0)
  20.                   then showmessage('Improper TOP entry.')
  21.                   else showmessage('Must enter TOP value.');
  22. end;
  23.  
  24. procedure TCmdForm.SetOfsInd(Sender: TObject);
  25. var
  26.   pf2: ParaFormat2;
  27.   x: double;
  28.   inpOK: boolean;
  29.   err: integer;
  30. begin
  31.   Val(OfsInd.Text, x, Err);
  32.   InpOK:= (Err=0);
  33.   if InpOK then
  34.      begin
  35.      FillChar(pf2, SizeOf(pf2), 0);
  36.      pf2.cbSize := SizeOf(PARAFORMAT2);
  37.      SendMessage(PageMemo.Handle, EM_GetParaFormat, 0, LParam(@pf2));
  38.      pf2.dwMask := PFM_STARTINDENT;  //PFM_STARTINDENT for absolute or PFM_OFFSETINDENT for relative to current
  39.      pf2.dxStartIndent:= round(x*20);  // note: PFM_STARTINDENT is easier to undo
  40.      SendMessage(PageMemo.Handle, EM_SETPARAFORMAT, 0, Longint(@pf2));
  41.      end else if (length(OfsInd.Text)>0)
  42.                   then showmessage('Improper IND entry.')
  43.                   else showmessage('Must enter IND value.')
  44. end;
  45.  
  46. procedure TCmdForm.SetOfsLft(Sender: TObject);  // relative to PFM_STARTINDENT, ie. negative or positive,
  47. var                                             // and a zero value is flush with PFM_STARTINDENT.
  48.   pf2: ParaFormat2;
  49.   x: double;
  50.   inpOK: boolean;
  51.   err: integer;
  52. begin
  53.   Val(OfsLft.Text, x, Err);
  54.   InpOK:= (Err=0);
  55.   if InpOK then
  56.      begin
  57.      FillChar(pf2, SizeOf(pf2), 0);
  58.      pf2.cbSize := SizeOf(PARAFORMAT2);
  59.      SendMessage(PageMemo.Handle, EM_GetParaFormat, 0, LParam(@pf2));
  60.      pf2.dwMask := PFM_OFFSET;
  61.      pf2.dxOffset:= round(x*20);
  62.      SendMessage(PageMemo.Handle, EM_SETPARAFORMAT, 0, Longint(@pf2));
  63.      end else if (length(OfsLft.Text)>0)
  64.                   then showmessage('Improper LFT entry.')
  65.                   else showmessage('Must enter LFT value.')
  66. end;
  67.  
  68. procedure TCmdForm.SetOfsRht(Sender: TObject);
  69. var
  70.   pf2: ParaFormat2;
  71.   x: double;
  72.   inpOK: boolean;
  73.   err: integer;
  74. begin
  75.   Val(OfsRht.Text, x, Err);
  76.   InpOK:= (Err=0);
  77.   if InpOK then
  78.      begin
  79.      FillChar(pf2, SizeOf(pf2), 0);
  80.      pf2.cbSize := SizeOf(PARAFORMAT2);
  81.      SendMessage(PageMemo.Handle, EM_GetParaFormat, 0, LParam(@pf2));
  82.      pf2.dwMask := PFM_RIGHTINDENT;
  83.      pf2.dxRightIndent:= round(x*20);
  84.      SendMessage(PageMemo.Handle, EM_SETPARAFORMAT, 0, Longint(@pf2));
  85.      end else if (length(OfsRht.Text)>0)
  86.                   then showmessage('Improper RHT entry.')
  87.                   else showmessage('Must enter RHT value.')
  88. end;
  89.  
  90. procedure TCmdForm.SetOfsBtm(Sender: TObject);
  91. var
  92.   pf2: ParaFormat2;
  93.   x: double;
  94.   inpOK: boolean;
  95.   err: integer;
  96. begin
  97.   Val(OfsBtm.Text, x, Err);
  98.   InpOK:= (Err=0);
  99.   if InpOK then
  100.      begin
  101.      FillChar(pf2, SizeOf(pf2), 0);
  102.      pf2.cbSize := SizeOf(PARAFORMAT2);
  103.      SendMessage(PageMemo.Handle, EM_GetParaFormat, 0, LParam(@pf2));
  104.      pf2.dwMask := PFM_SPACEAFTER;
  105.      pf2.dySpaceAfter:= round(x*20);
  106.      SendMessage(PageMemo.Handle, EM_SETPARAFORMAT, 0, Longint(@pf2));
  107.      end else if (length(OfsBtm.Text)>0)
  108.                   then showmessage('Improper BTM entry.')
  109.                   else showmessage('Must enter BTM value.')
  110. end;
  111.  
  112. procedure TCmdForm.SetOfsLns(Sender: TObject);  // multiple to base height extending down from top
  113. var
  114.   pf2: ParaFormat2;
  115.   x: double;
  116.   inpOK: boolean;
  117.   err: integer;
  118. begin
  119.   Val(OfsLns.Text, x, Err);
  120.   InpOK:= (Err=0);
  121.   if InpOK then
  122.      begin
  123.      // initialize pf2
  124.      FillChar(pf2, SizeOf(pf2), 0);
  125.      pf2.cbSize := SizeOf(PARAFORMAT2);
  126.      SendMessage(PageMemo.Handle, EM_GetParaFormat, 0, LParam(@pf2));
  127.      pf2.dwMask := PFM_LINESPACING;
  128.  
  129.      if (LnsMultiply.checked=true) then // factored descent
  130.         begin
  131.         pf2.bLineSpacingRule := 5;
  132.         pf2.dyLineSpacing:= round(x*20);
  133.         SendMessage(PageMemo.Handle, EM_SETPARAFORMAT, 0, Longint(@pf2));
  134.         end;
  135.      if (LnsAtLeast.checked=true) then  // minimum ascent
  136.         begin
  137.         pf2.bLineSpacingRule := 3;
  138.         pf2.dyLineSpacing:= round(x*20);
  139.         SendMessage(PageMemo.Handle, EM_SETPARAFORMAT, 0, Longint(@pf2));
  140.         end;
  141.      if (LnsExactly.checked=true) then  // maximum ascent
  142.         begin
  143.         pf2.dwMask := PFM_LINESPACING;
  144.         pf2.bLineSpacingRule := 4;
  145.         pf2.dyLineSpacing:= round(x*20);
  146.         SendMessage(PageMemo.Handle, EM_SETPARAFORMAT, 0, Longint(@pf2));
  147.         end;
  148.      end else if (length(OfsLns.Text)>0)
  149.                  then showmessage('Improper LNS entry.')
  150.                  else showmessage('Must enter LNS value.')
  151. end;  
  152.  

Rick
Title: Re: Paragraph Formatting and Syriac fonts
Post by: Thaddy on April 01, 2017, 09:27:15 pm
I would suggest the following, but it is a matter of taste:
Code: Pascal  [Select]
  1. // FillChar(pf2, SizeOf(pf2), 0);
  2. pf2 := Default(Paraformat2);
Default() is a compiler intrinsic introduced in FPC 3.0.0. It zero's the memory for the size of the underlying type.
Title: Re: Paragraph Formatting and Syriac fonts
Post by: rick2691 on April 03, 2017, 07:12:40 pm
Thaddy,

Thanks for the input. Unfortunately, I am using FPC 2.4.4. FPC 3.0.0 still has problems with Windows XP.

Rick