Text to images Generator (Console app generating bitmaps with text) lazarus / 39-Graphics ---------------------------------------------------------------------- barracuda · 2023-10-28 06:33 Hello, I am using mint 20 and my question is about creating series of text for test purposes. I have given the question to chatGPT but he simply has not senseful answer. The problem is like that: I need to create text like "A word". But the image must be the dimentions of the word image plus some 2 or 4px around it. I originally tried to write to program in AHK for Windows XP (old version) and found no support for this old app. Maybe I could try Freepascal. I think even the chatGPT could do that but it has no idea how to solve the problem with measuring of the text of the graphic context which does not exist. And it can not to create the graphic context because he does not know the width and height of the image... So how to solve this "loop" issue? As I said, I have tried to write the code in AHK so I will try to explain it on the piece of code of it. I have fonts and texts I want to create. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i fonts := ["Verdana", "Roboto", "Arial", "Helvetica", "sans-serif"]texts := ["Psalm 91:1", "Slovo", "AHK pro", "Vygenerování zkušebních obrázků.", "Vygenerování zkušebních obrázků.", "Včera ráno, dnes odpoledne a v neděli večer"]fontSize := 22.4 I create dir.. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i If !FileExist("TestImages_22.4px")    FileCreateDir, TestImages_22.4px Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Then there is loop for every font.for font in fonts {font := Gdip_FontCreate(font, fontSize) and neste loop for every text Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i for text in texts {Now ChatGPT suggested some shitty code to create image of size 1x1 px - seems nonsense for me Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i image := Gdip_CreateBitmap(1, 1) ; Vytvoření prázdného obrázku pro MeasureStringG := Gdip_GraphicsFromImage(image) G is 0 Or this buggy shi.. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i M := Gdip_MeasureString(0, text, font, 0, &RectF)        textWidth := M.width Where it used pointer 0 for Graphic Context. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i and then the map should be createdCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i image := Gdip_CreateBitmap(M.Width, M.Height)And the rest of code inside the loop is Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i         fileName := font . " " . StrSplit(text, " ")[1] . ".png"        pBrush := Gdip_BrushCreateSolid(0x55FFFFFF) ; Černé pozadí        Gdip_FillRectangle(G, pBrush, 0, 0, textWidth, y + fontSize) ; Vykreslení pozadípBrush := Gdip_BrushCreateSolid(0xFF000000) ; Černé pozadíGdip_DrawString(G, text, font, textBrush, x, y)Gdip_SaveBitmapToFile(image, fileName)Gdip_DisposeImage(image)Gdip_D eleteBrush(pBrush)Gdip_DeleteStringFormat(hFormat)Gdip_DeleteFont(hFon t)Gdip_DeleteFontFamily(hFamily)  I think this should not be hard to do in any programming language and I can create it on linux and copy the files to Windows then. But what is your idea of solving the problem with measuring of non existing image text? ---------------------------------------------------------------------- wp · 2023-10-28 09:16 Don't waste your time with ChatGPT... Measuring text size is one of the standard methods of the LCL canvas: Canvas.TextSize(text), the result is a TSize record with the elements CX (width) and CY (height). The following code snippet creates a bitmap, sets the font, measures the text, adjusts the bitmap size to the text size + some margin, draws the text and saves the bitmap to file. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses  Types, LCLType, LCLIntf; { TForm1 } procedure TForm1.Button1Click(Sender: TObject);const  MARGIN = 3;  TXT = 'A word';var  bmp: TBitmap;  ext: TSize;  R: TRect;begin  bmp := TBitmap.Create;  try    // Set the font and measure the text size    bmp.Canvas.Font.Name := 'Arial';    bmp.Canvas.Font.Size := 24;    bmp.Canvas.Font.Color := clRed;    bmp.Canvas.Font.Style := [fsItalic];    R.TopLeft := Point(0, 0);    R.BottomRight := TPoint(bmp.Canvas.TextExtent(TXT));    // Set the size of the bitmap (= text size + margin)    bmp.SetSize(R.Right + 2*MARGIN, R.Bottom + 2*MARGIN);    // Paint the bitmap's background color    bmp.Canvas.Brush.Color := clWhite;    bmp.Canvas.FillRect(0, 0, bmp.Width, bmp.Height);    // Draw the text    bmp.Canvas.TextOut(MARGIN, MARGIN, TXT);    // Save bitmap to file    bmp.SaveToFile('A_Word.bmp');  finally    bmp.Free;  end;end; ---------------------------------------------------------------------- barracuda · 2023-10-28 09:27 Thank you but I don't remember how to create  new program. Should I create new Unit? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit3; {$mode ObjFPC}{$H+} interface uses ... ---------------------------------------------------------------------- dseligo · 2023-10-28 09:39 Quote from: barracuda on October 28, 2023, 11:27:45 amThank you but I don't remember how to create  new program. Should I create new Unit? ? https://wiki.freepascal.org/Lazarus_Tutorial#Your_first_Lazarus_Progra m.21 ---------------------------------------------------------------------- wp · 2023-10-28 10:04 Quote from: barracuda on October 28, 2023, 11:27:45 amThank you but I don't remember how to create  new program. Should I create new Unit? In Lazarus: "File" > "New" > "Project" > "Application" > "OK"In the component palette click on the icon of TButton (the third one on the "Standard" palette). Then click on the form at the point where you want the button to be.Double-click on the button on the form. The code editor appears with an empty ButtonClick procedure. From my previous post, copy the code between ("procedure TForm1.ButtonClick(...)") and "end;" and paste it over the existing empty procedure in your code editor. Add the units Types, LCLType, LCLIntf to the uses clause of the form.Press F9 to compile and run ---------------------------------------------------------------------- barracuda · 2023-10-28 14:52 Thank you I got error near declaration of bmp and first use of type TRect . unit2.pas(40,12) Fatal: Syntax error, ";" expected but "identifier BMP" found I don't see whats wrong Lazarus 2.2.6 Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit unit2; {$mode ObjFPC}{$H+} interface uses  Classes, SysUtils, Forms, Controls,  Graphics, Dialogs, StdCtrls,  Types, LCLType, LCLIntf; type   { TForm2 }   TForm2 = class(TForm)    Button1: TButton;    procedure Button1Click(Sender: TObject);  private   public   end; var  Form2: TForm2; implementation {$R *.lfm} { TForm2 } procedure TForm2.Button1Click(Sender: TObject);  const MARGIN = 3;  const TXT = 'A word';var  bmp: TBitmap;  ext: TSize;  R: TRect;begin  bmp := TBitmap.Create;  try    // Set the font and measure the text size    bmp.Canvas.Font.Name := 'Arial';    bmp.Canvas.Font.Size := 24;    bmp.Canvas.Font.Color := clRed;    bmp.Canvas.Font.Style := [fsItalic];    R.TopLeft := Point(0, 0);    R.BottomRight := TPoint(bmp.Canvas.TextExtent(TXT));    // Set the size of the bitmap (= text size + margin)    bmp.SetSize(R.Right + 2*MARGIN, R.Bottom + 2*MARGIN);    // Paint the bitmap's background color    bmp.Canvas.Brush.Color := clWhite;    bmp.Canvas.FillRect(0, 0, bmp.Width, bmp.Height);    // Draw the text    bmp.Canvas.TextOut(MARGIN, MARGIN, TXT);    // Save bitmap to file    bmp.SaveToFile('A_Word.bmp');  finally    bmp.Free;  end;end; end. ---------------------------------------------------------------------- wp · 2023-10-28 16:49 I don't know - the code is compiling for me... If it still is not working pack the .pas, .lfm, .lpi and .lpr files into a common zip and upload that here ("Attachments and other options") so that I can test the entire project. ---------------------------------------------------------------------- barracuda · 2023-10-28 17:01 I'll try the code when I will be on Windows, maybe it will compile from there. Thank you for afford. ---------------------------------------------------------------------- wp · 2023-10-28 18:07 The code is not specific for Windows, it should compile under other operating systems, too. ---------------------------------------------------------------------- barracuda · 2023-10-29 11:27 Thank you. I have tested it on Windows and no problem. The only problem what I had because I am a newbie using Lazarus, that I have added the unit to already existing project because the old project was opened and I did not close it. It seem that next time I must close the project from Project menu and then select New Project from the same menu... So I will start linux now because I need to download the fonts which I cannot download on the Windows XP and I can try again to compile it on Linux Mint - Lazarus... and I will ask chatGPT to do the rest of job. ---------------------------------------------------------------------- Handoko · 2023-10-29 11:46 If you haven't known, ChatGPT is stupid in Pascal programming. Better ask me or anyone in the forum. ---------------------------------------------------------------------- barracuda · 2023-10-29 14:56 I know he is stupid, often it produces irritating results but sometimes a bit more time of tests can bring new questions and new findings... I know my way of learning is strange, but sometimes the code if just find for very basic jobs. If I would bother you will all question, it could be waste of your time... ---------------------------------------------------------------------- Handoko · 2023-10-29 15:57 Not wasting my time. I started learning programming at the beginning of the 90s. There was no internet and programming books were rare to be found. Computing was not taught in schools, I couldn't talk it with my teachers and friends. Computers were very expensive, even some of my friends had it they're not interested in programming. I regularly visited my local bookshops so I won't miss any good books, but almost all of them were for C/C++ and BASIC users. Programming is hard, there are too many topics to study. I am still learning it. Programming is not my job but I always try to spend some time for it. I appreciate people who has the passion for learning. And here I am, to help anyone who want to learn Lazarus.  It's not rare, some users even privately contact me via my email even whatsapp me. Don't hesitate ask, if you have problem in using Lazarus or Pascal. Because that's what this forum made for. ---------------------------------------------------------------------- barracuda · 2023-10-29 22:33 Quote from: Handoko on October 29, 2023, 04:57:06 pmNot wasting my time. I started learning programming at the beginning of the 90s. There was no internet and programming books were rare to be found. Computing was not taught in schools, I couldn't talk it with my teachers and friends. Computers were very expensive, even some of my friends had it they're not interested in programming. I regularly visited my local bookshops so I won't miss any good books, but almost all of them were for C/C++ and BASIC users. Programming is hard, there are too many topics to study. I am still learning it. Programming is not my job but I always try to spend some time for it. I appreciate people who has the passion for learning. And here I am, to help anyone who want to learn Lazarus.  It's not rare, some users even privately contact me via my email even whatsapp me. Don't hesitate ask, if you have problem in using Lazarus or Pascal. Because that's what this forum made for. OK I tried my nerves with chatGPT. This is what he produced. Will it save some time for you? I tried to make him to create the arrays and loops and it should also create the folders, which instruction he probably skipped. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1;{$mode objfpc}{$H+} interface uses  Classes, SysUtils, Forms, Graphics,  LCLType, LCLIntf; type  { TForm1 }  TForm1 = class(TForm)  procedure FormCreate(Sender: TObject);  private    { Private declarations }    procedure GenerateImages(fonts: array of string; texts: array of string; fontSize: Integer; folderName: string);  public    { Public declarations }  end; var  Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.GenerateImages(fonts: array of string; texts: array of string; fontSize: Integer; folderName: string);var  bmp: TBitmap;  rect: TRect;  fileName: string;  i, j: Integer;begin  bmp := TBitmap.Create;  try    for i := 0 to High(fonts) do    begin      for j := 0 to High(texts) do      begin        bmp.Width := 500;        bmp.Height := 100;        bmp.Canvas.Font.Name := fonts[i];        bmp.Canvas.Font.Size := fontSize;         fileName := IncludeTrailingPathDelimiter(folderName) + fonts[i] + ' ' + Copy(texts[j], 1, Pos(' ', texts[j]) - 1) + '.bmp';         bmp.Canvas.Brush.Color := clWhite;        bmp.Canvas.FillRect(bmp.Canvas.ClipRect);        bmp.Canvas.TextOut(0, 0, texts[j]);        bmp.SaveToFile(fileName);      end;    end;  finally    bmp.Free;  end;end; procedure TForm1.FormCreate(Sender: TObject);begin  // First batch  var fonts1 := ['Verdana', 'Roboto', 'Arial', 'Helvetica', 'sans-serif'];  var texts1 := ['Psalm 91:1', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer'];  var fontSize1 := 22;  var folderName1 := 'TestImages_22.4px';   GenerateImages(fonts1, texts1, fontSize1, folderName1);  ShowMessage('První dávka zkušebních obrázků (22.4px) byla uložena do složky "' + folderName1 + '".');   // Second batch  var fonts2 := ['Verdana', 'Roboto', 'Arial', 'Helvetica', 'sans-serif'];  var texts2 := ['Text analysis', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer'];  var fontSize2 := 16;  var folderName2 := 'TestImages_16px';   GenerateImages(fonts2, texts2, fontSize2, folderName2);  ShowMessage('Druhá dávka zkušebních obrázků (16px) byla uložena do složky "' + folderName2 + '".');   // Third batch  var fonts3 := ['Arial', 'Helvetica', 'Sans-serif'];  var texts3 := ['Strong''s', 'Hebrew', 'English', 'Morphology', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer'];  var fontSize3 := 12;  var folderName3 := 'TestImages_12px';   GenerateImages(fonts3, texts3, fontSize3, folderName3);  ShowMessage('Třetí dávka zkušebních obrázků (12px) byla uložena do složky "' + folderName3 + '".');   // Fourth batch  var fonts4 := ['Arial', 'Helvetica', 'Sans-serif'];  var texts4 := ['Go to Parallel Hebrew', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer'];  var fontSize4 := 16;  var folderName4 := 'TestImages_16px';   GenerateImages(fonts4, texts4, fontSize4, folderName4);  ShowMessage('Čtvrtá dávka zkušebních obrázků (16px) byla uložena do složky "' + folderName4 + '".');end; end.  Frustrated enough tonight. This is the plan I have created for chatGPT, where the main idea is presented... Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i fonts := ["Verdana", "Roboto", "Arial", "Helvetica", "sans-serif"]texts := ["Psalm 91:1", "Slovo", "AHK pro", "Vygenerování zkušebních obrázků.", "Vygenerování zkušebních obrázků.", "Včera ráno, dnes odpoledne a v neděli večer"]fontSize := 22.4 If !FileExist("TestImages_22.4px")    FileCreateDir, TestImages_22.4px for font in fonts    for text in texts        MsgBox, 1st batch (22.4px) saved "TestImages_22.4px". ; Druhá dávkafonts := ["Verdana", "Roboto", "Arial", "Helvetica", "sans- serif"]texts := ["Text analysis", "Slovo", "AHK pro", "Vygenerování zkušebních obrázků.", "Vygenerování zkušebních obrázků.", "Včera ráno, dnes odpoledne a v neděli večer"]fontSize := 16If !FileExist("TestImages_16px")    FileCreateDir, TestImages_16pxfor font in fonts     for text in texts MsgBox, 2nd batch (16px) saved to folder "TestImages_16px". ; Třetí dávka - Velikost 12px pro Arial, Helvetica, Sans-serif:fonts := ["Arial", "Helvetica", "Sans- serif"]texts := ["Strong's", "Hebrew", "English", "Morphology", "Slovo", "AHK pro", "Vygenerování zkušebních obrázků.", "Vygenerování zkušebních obrázků.", "Včera ráno, dnes odpoledne a v neděli večer"]fontSize := 12If !FileExist("TestImages_12px")    FileCreateDir, TestImages_12px for font in fonts    for text in textsMsgBox, 3td batch  (12px) saved to folder "TestImages_12px". ; Čtvrtá dávka - Velikost 16px pro Arial, Helvetica, Sans-serif:fonts := ["Arial", "Helvetica", "Sans-serif"]texts := ["Go to Parallel Hebrew", "Slovo", "AHK pro", "Vygenerování zkušebních obrázků.", "Vygenerování zkušebních obrázků.", "Včera ráno, dnes odpoledne a v neděli večer"]fontSize := 16If !FileExist("TestImages_16px")    FileCreateDir, TestImages_16pxfor font in fonts      for text in texts {MsgBox, 4th batch (16px) saved  My brain is not so flexible to learn, getting older. So any help appreciated. Tomorrow I will be more relaxed to spend more time with it. ---------------------------------------------------------------------- Handoko · 2023-10-30 06:43 wp's code should work. But if you have problem using it, you can download this fully working code: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, Forms, Graphics, StdCtrls, Types; type   { TForm1 }   TForm1 = class(TForm)  Button1: TButton;    Memo1: TMemo;    procedure Button1Click(Sender: TObject);    procedure FormCreate(Sender: TObject);  private    function  FontIsAvailable(const FontName: string): Boolean;    procedure Generate(const FontName: string; FontSize: Integer;      MarginX, MarginY: Integer; const Message, FileName: string);  end; var  Form1: TForm1; implementation const  Names:    array[0..2] of string = ('img1.bmp', 'img2.bmp', 'img3.bmp');  Fonts:    array[0..2] of string = ('Dejavu Sans', 'Arial', 'FreeMono');  Messages: array[0..2] of string = ('Lazarus is fun.',                                   'Let''s learn programming!',                                   'Hooray'); {$R *.lfm} { TForm1 } procedure TForm1.Button1Click(Sender: TObject);var  FontName: string;  i: Integer;begin  for i := Low(Names) to High(Names) do  begin    FontName := Fonts[i];    if not(FontIsAvailable(FontName)) then      Memo1.Lines.Add(Fonts[i] + ' is not available.');    Generate(FontName, 12, 8, 4, Messages[i], Names[i]);  end;  Memo1.Lines.Add(IntToStr(Length(Names)) + ' images generated.');end; procedure TForm1.FormCreate(Sender: TObject);begin  Memo1.Clear;end; function TForm1.FontIsAvailable(const FontName: string): Boolean;begin  Result := Screen.Fonts.IndexOf(FontName) >= 0;end; procedure TForm1.Generate(const FontName: string; FontSize: Integer; MarginX,  MarginY: Integer; const Message, FileName: string);var  Output: TBitmap;  Size:   TSize;  SizeX:  Integer;  SizeY:  Integer;begin  Output := TBitmap.Create;  Output.SetSize(100, 100);  Output.Canvas.Brush.Style := bsClear;  Output.Canvas.Font.Name := FontName;  Output.Canvas.Font.Size := FontSize;  Size  := Output.Canvas.TextExtent(Message);  SizeX := MarginX + Size.Width + MarginX;  SizeY := MarginY + Size.Height + MarginY;  Output.SetSize(SizeX, SizeY);  Output.Canvas.Brush.Color := clWhite;  Output.Canvas.FillRect(0, 0, SizeX, SizeY);  Output.Canvas.TextOut(MarginX, MarginY, Message);  Output.SaveToFile(FileName);  Output.Free;end; end. You can copy/paste the Generate procedure for used in other projects, see line #66.Not sure on other platforms but on Ubuntu Mate, wp's code need to have brush style to be set before calling TextExtent, see line #76.Linux does not have same default fonts installed as in Windows. So, you can use FontIsAvailable function to check the availability, see line #61.You can change the image's names, fonts and texts in line #31 .. #33. ---------------------------------------------------------------------- barracuda · 2023-10-30 09:23 In Windows XP, I tried to create new project for console program and I got errors that libraries not found. Why? Are these not present with console apps? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program project1; {$mode objfpc}{$H+} uses  {$IFDEF UNIX}  cthreads,  {$ENDIF}  Classes, SysUtils, CustApp,   Graphics,  LCLType, LCLIntf;{ you can add units after this }; type ---------------------------------------------------------------------- Handoko · 2023-10-30 10:12 In console mode, many features will be disabled by default. If you want to enable Graphics unit in console mode, do this: Lazarus main menu > Project > Project Inspector > Add > New Requirement > type: LCLBase > select the LCLBase and click the OK button ---------------------------------------------------------------------- barracuda · 2023-10-30 20:05 I am trying to retype the code to console program. An error appeared near first batch of constants: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i constructor TMyApplication.Create(TheOwner: TComponent);inherited Create(TheOwner);// Prvníconst  fonts1: array[0..4] of string = ('Verdana', 'Roboto', 'Arial', 'Helvetica', 'sans-serif');  Originally chatGPT used ahk syntax so I corrected. But the error is begin is expected after inherited Create(TheOwner); so there should be the constants defined? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program project1; {$mode objfpc}{$H+} uses  {$IFDEF UNIX}  cthreads,  {$ENDIF}  Classes, SysUtils, CustApp,  Graphics,  LCLType, LCLIntf; type   { TMyApplication }   TMyApplication = class(TCustomApplication)  protected    procedure DoRun; override;  private    { Private declarations }    procedure GenerateImages(fonts: array of string; texts: array of string; fontSize: Integer; folderName: string);  public    constructor Create(TheOwner: TComponent); override;    destructor Destroy; override;    procedure WriteHelp; virtual;  end; { TMyApplication } procedure TMyApplication.DoRun;  var ErrorMsg: String;begin  // quick check parameters  ErrorMsg:=CheckOptions('h', 'help');  if ErrorMsg<>'' then begin    ShowException(Exception.Create(ErrorMsg));    Terminate;    Exit;  end;   // parse parameters  if HasOption('h', 'help') then begin    WriteHelp;    Terminate;    Exit;  end;   { add your program here }   // stop program loop  Terminate;end; constructor TMyApplication.Create(TheOwner: TComponent);inherited Create(TheOwner);// Prvníconst  fonts1: array[0..4] of string = ('Verdana', 'Roboto', 'Arial', 'Helvetica', 'sans-serif');  texts1: array[0..5] of string = ('Psalm 91:1', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');  fontSize1: Integer = 22;  folderName1: string = 'TestImages_22.4px'; // Druhýconst  fonts2: array[0..4] of string = ('Verdana', 'Roboto', 'Arial', 'Helvetica', 'sans-serif');  texts2: array[0..5] of string = ('Text analysis', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');  fontSize2: Integer = 16;  folderName2: string = 'TestImages_16px'; // Třetíconst  fonts3: array[0..2] of string = ('Arial', 'Helvetica', 'Sans-serif');  texts3: array[0..8] of string = ('Strong''s', 'Hebrew', 'English', 'Morphology', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');  fontSize3: Integer = 12;  folderName3: string = 'TestImages_12px';constconst  fonts4: array[0..2] of string = ('Arial', 'Helvetica', 'Sans-serif');  texts4: array[0..5] of string = ('Go to Parallel Hebrew', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');  fontSize4: Integer = 16;  folderName4: string = 'TestImages_16px';   StopOnException:=True;  GenerateImages(fonts1, texts1, fontSize1, folderName1);  ShowMessage('První dávka zkušebních obrázků (22.4px) byla uložena do složky "' + folderName1 + '".');  GenerateImages(fonts2, texts2, fontSize2, folderName2);  ShowMessage('Druhá dávka zkušebních obrázků (16px) byla uložena do složky "' + folderName2 + '".');  GenerateImages(fonts3, texts3, fontSize3, folderName3);  ShowMessage('Třetí dávka zkušebních obrázků (12px) byla uložena do složky "' + folderName3 + '".');  GenerateImages(fonts4, texts4, fontSize4, folderName4);  ShowMessage('Čtvrtá dávka zkušebních obrázků (16px) byla uložena do složky "' + folderName4 + '".');end; destructor TMyApplication.Destroy;begin  inherited Destroy;end; procedure TMyApplication.WriteHelp;begin  { add your help code here }  writeln('Usage: ', ExeName, ' -h');end; procedure TMyApplication.GenerateImages(fonts: array of string; texts: array of string; fontSize: Integer; folderName: string);  var    bmp: TBitmap;    rect: TRect;    fileName: string;    i, j: Integer;begin  bmp := TBitmap.Create;  try    for i := 0 to High(fonts) do    begin      for j := 0 to High(texts) do      begin        bmp.Width := 500;        bmp.Height := 100;        bmp.Canvas.Font.Name := fonts[i];        bmp.Canvas.Font.Size := fontSize;         fileName := IncludeTrailingPathDelimiter(folderName) + fonts[i] + ' ' + Copy(texts[j], 1, Pos(' ', texts[j]) - 1) + '.bmp';         bmp.Canvas.Brush.Color := clWhite;        bmp.Canvas.FillRect(bmp.Canvas.ClipRect);        bmp.Canvas.TextOut(0, 0, texts[j]);        bmp.SaveToFile(fileName);      end;    end;  finally    bmp.Free;  end;end; var  Application: TMyApplication;begin  Application:=TMyApplication.Create(nil);  Application.Title:='My Application';  Application.Run;  Application.Free;end. ---------------------------------------------------------------------- Handoko · 2023-10-31 02:44 For making easier to understand, below I provide 2 examples: Example 1 (the buggy code): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i constructor TMyApplication.Create(TheOwner: TComponent);inherited Create(TheOwner);const  fontSize1: Integer = 22;end; Example 2 (the correct code): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i constructor TMyApplication.Create(TheOwner: TComponent);const  fontSize1: Integer = 22;begin  inherited Create(TheOwner);end; Can you see the differences? It must have a begin statement, see Example 2 Line #4.The inherited statement must place between begin - end block, see Example 2 Line #5. ---------------------------------------------------------------------- barracuda · 2023-10-31 07:16 OK, fine. I didn't know what was the original code. ---------------------------------------------------------------------- barracuda · 2023-10-31 09:33 Quote from: Handoko on October 30, 2023, 07:43:07 amCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function TForm1.FontIsAvailable(const FontName: string): Boolean;begin  Result := Screen.Fonts.IndexOf(FontName) >= 0;end;  What do I must to include to have the Screen a part of the console project? ---------------------------------------------------------------------- Handoko · 2023-10-31 09:53 Screen object is provided by Forms unit, and Forms unit is provided by LCL package. So you need to add LCL package as required packaged in the Object Inspector and then add Forms in the uses clause. ---------------------------------------------------------------------- barracuda · 2023-10-31 10:34 Now I have this warning when I try to compile, this is dialoge window warning. The (console) project does not use the LCL unit interfaces which is required by LCL base. You will get strange linker errors if you use it without LCL unit interfaces. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i LCL base is added. program project1; {$mode objfpc}{$H+} uses  {$IFDEF UNIX}  cthreads,  {$ENDIF}  Classes, SysUtils, CustApp,  Graphics,  LCLType, LCLIntf,  Forms; // required for Screen unit  Is there different way to check if the font exists in console app? ---------------------------------------------------------------------- Handoko · 2023-10-31 14:25 In console mode, as I already said earlier, many features will be disabled. Is there any reason you want to write the program in console mode? I rarely write console programs, I am not familiar how to solve you problem. But hey, you can modify the 'normal' application to behave like a console program: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, Forms, Graphics, StdCtrls, Types; type   { TForm1 }   TForm1 = class(TForm)  procedure FormActivate(Sender: TObject);  private    function  FontIsAvailable(const FontName: string): Boolean;    procedure Generate(const FontName: string; FontSize: Integer;      MarginX, MarginY: Integer; const Message, FileName: string);  end; var  Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.FormActivate(Sender: TObject);begin  Hide;  Generate('Arial', 22, 4, 2, 'Test', 'test.bmp');  Close;end; function TForm1.FontIsAvailable(const FontName: string): Boolean;begin  Result := Screen.Fonts.IndexOf(FontName) >= 0;end; procedure TForm1.Generate(const FontName: string; FontSize: Integer; MarginX,  MarginY: Integer; const Message, FileName: string);var  Output: TBitmap;  Size:   TSize;  SizeX:  Integer;  SizeY:  Integer;begin  Output := TBitmap.Create;  Output.SetSize(100, 100);  Output.Canvas.Brush.Style := bsClear;  Output.Canvas.Font.Name := FontName;  Output.Canvas.Font.Size := FontSize;  Size  := Output.Canvas.TextExtent(Message);  SizeX := MarginX + Size.Width + MarginX;  SizeY := MarginY + Size.Height + MarginY;  Output.SetSize(SizeX, SizeY);  Output.Canvas.Brush.Color := clWhite;  Output.Canvas.FillRect(0, 0, SizeX, SizeY);  Output.Canvas.TextOut(MarginX, MarginY, Message);  Output.SaveToFile(FileName);  Output.Free;end; end. To make a normal application to behave like a console program: Add an event for Form.Activate, see line #31.At the beginning of the block, put Hide; command, see line #33.At the end of the block, put Close; command, see line #35.Tested on Linux only, but I believe it should work on Windows too. ---------------------------------------------------------------------- bytebites · 2023-10-31 15:06 Alternativeliy. this is lpr-file. Dont create form at all. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1; {$mode objfpc}{$H+} uses  {$IFDEF UNIX}  cthreads,  {$ENDIF}  {$IFDEF HASAMIGA}  athreads,  {$ENDIF}  Interfaces, // this includes the LCL widgetset  Forms, Graphics, StdCtrls, Types  { you can add units after this }; {$R *.res} //begin//  RequireDerivedFormResource:=True;//  Application.Scaled:=True;//  Application.Initialize;//  //Application.CreateForm(TForm1, Form1);//  Application.Run;//end. function FontIsAvailable(const FontName: string): Boolean;begin  Result := Screen.Fonts.IndexOf(FontName) >= 0;end; procedure Generate(const FontName: string; FontSize: Integer; MarginX,  MarginY: Integer; const Message, FileName: string);var  Output: TBitmap;  Size:   TSize;  SizeX:  Integer;  SizeY:  Integer;begin  Output := TBitmap.Create;  Output.SetSize(100, 100);  Output.Canvas.Brush.Style := bsClear;  Output.Canvas.Font.Name := FontName;  Output.Canvas.Font.Size := FontSize;  Size  := Output.Canvas.TextExtent(Message);  SizeX := MarginX + Size.Width + MarginX;  SizeY := MarginY + Size.Height + MarginY;  Output.SetSize(SizeX, SizeY);  Output.Canvas.Brush.Color := clWhite;  Output.Canvas.FillRect(0, 0, SizeX, SizeY);  Output.Canvas.TextOut(MarginX, MarginY, Message);  Output.SaveToFile(FileName);  Output.Free;end; begin  Generate('Arial', 22, 4, 2, 'Test', 'test.bmp');end. ---------------------------------------------------------------------- wp · 2023-10-31 15:42 bytebites was faster... Nevertheless I am adding my own solution which is practically identical with bytebites' code, except for the loops over all fonts and all texts. Please note that displaying utf8-encoded text on the Windows console is always some kind of adventure, and some older versions of Windows do not handle utf8 at all. I am on Windows-11 where it is working flawlessly (although the command "mode con" lists codepage 850 to be active - I gave up trying to understand all this chaos...). ---------------------------------------------------------------------- barracuda · 2023-10-31 17:35 Thank you for your answers, today I am exhausted and I would like to react and try your code tomorow. The reason why I try to write a console program is that that takes less resources and in Mint I have small memory. So this would be new experience for me. I think I never wrote console program in Delphi 7 and not with FreePascal/Lazarus either. I still learn. ---------------------------------------------------------------------- wp · 2023-10-31 22:21 Quote from: barracuda on October 31, 2023, 06:35:31 pmThe reason why I try to write a console program is that that takes less resources and in Mint I have small memory. But the approach of using the LCL discussed so far means that memory usage will be quite high.  The ImagesOfTexts program that I posted above has almost 2 MB on the disk (after stripping the debug information), and this is almost as large as a GUI application. If low memory usage is a key requirement you must address the project in a different way: do not use the LCL, but use the units of the fcl-image package which is part of the FPC installation. An example for drawing text by this library is given in the wiki: https://wiki.freepascal.org/fcl-image#Drawing_text. Based on this example I modified my earlier sample code to use fcl-image rather than LCL. The exe now is only 300 kB... Tested on Windows, Linux Mint and macOS Mojave (you may have to review the list of fonts and font directories). ---------------------------------------------------------------------- barracuda · 2023-11-01 16:33 Guys now I cannot check your code, I think I will be back on friday, some urgent issues I have to solve. If the approach is bad and high memory consumption so maybe lets go back to the Window application. But I will enjoy to check both approaches. ---------------------------------------------------------------------- barracuda · 2023-11-03 12:18 Quote from: bytebites on October 31, 2023, 04:06:02 pmAlternativeliy. this is lpr-file. Dont create form at all. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1;...  Stupid question, but why the code is saved in .lpr file and not in .pas? Does .lpr mean that this is the main .pas file, the main app code? I dont know the meaning of LPR abbr. Sorry. ---------------------------------------------------------------------- Handoko · 2023-11-03 12:38 Lazarus := Pascal + IDE + libraries Because Lazarus offers a lot of extra things than a simple tool for writing a small program, it needs to separate the project into multiple smaller files. A medium size project usually can contain more than thousands lines of code, it is not wise to store them in a single pas file. Read more: https://wiki.freepascal.org/File_extensions#Lazarus ---------------------------------------------------------------------- barracuda · 2023-11-03 12:44 I have saved the old project of mine, so it was suggested to add interface unit. I've just added this code: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i writeln(folderName);if not DirectoryExists(folderName) then          mkdir(folderName);  before the line: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i fileName := IncludeTrailingPathDelimiter(folderName) So the nice thing is that everything compiled without errors and generated the directories and images. All looks great. I think I have one mistake in the algo, because the directories with Helvetica should not be create because I don't have this commertial font (I am not sure if it is possible to download it for free). I am gonna to check your posted programs too. ---------------------------------------------------------------------- wp · 2023-11-03 13:57 Quote from: barracuda on November 03, 2023, 01:18:55 pmStupid question, but why the code is saved in .lpr file and not in .pas? Does .lpr mean that this is the main .pas file, the main app code? I dont know the meaning of LPR abbr. There are no "stupid questions"... This project was written with Lazarus, and Lazarus is organized in projects. The program file by default gets the extension .lpr ("lazarus project"), you could use .pas or .pp as well (or - I think - any other extension), but the different extension makes it easier to find the main project file in the file manager. There is also an xml file with the extension .lpi which lists which file is the project file and which other files belong to the project - it can be missing, but then it has to be recreated manually by the user which may requite quite some effort when there are lots of other units. ---------------------------------------------------------------------- barracuda · 2023-11-03 13:57 Where can I save My program command line options (paramStr) of current project? E.g. I will call the program like this: createimages.exe drive=a: color=black bgcolor=white so for purposes of debuging this should be saved: "drive=a: color=black bgcolor=white" ---------------------------------------------------------------------- wp · 2023-11-03 14:24 Command line options are entered by the user, the are not stored in the application. But if, for testing, you need the same parameters again and again and you do not want to type them in every time, you can go to "Run" > "Run Parameters" and specify the parameters in the line "Command line parameters". This is a history list, btw, i.e. you can keep several sets of parameters here. But just to make sure: they are accessible only while working in the IDE. Outside the IDE the parameters must be typed in the command line. ---------------------------------------------------------------------- barracuda · 2023-11-03 15:26 How to convert String color to TGraphics.Color . This is because now I am processing the arguments from command line and this generatles color (of sure): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i         if s.color <> '' then          bmp.Canvas.Font.Color := s.color;        else          bmp.Canvas.Font.Color := clBlack;         if s.backgrounColor <> '' then          bmp.Canvas.Font.Color := s.backgroundColor;        else          bmp.Canvas.Font.Color := clBlack; ---------------------------------------------------------------------- Handoko · 2023-11-03 15:31 Use StringToColor function. https://lazarus- ccr.sourceforge.io/docs/lcl/graphics/stringtocolor.html ---------------------------------------------------------------------- barracuda · 2023-11-03 16:19 I am adding some improvements to code (not fully finnished): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TSetInfo = record    Drive: string;    Path: string;    Color: TColor;    BackgroundColor: TColor;    Italic: Boolean;    Bold: Boolean;  end; procedure TMyApplication.ProccessArguments(); var i: integer; strA: Array of String;begin  s.Italic := False;  s.Bold := False;  s.Color:=clWhite;  s.BackgroundColor:=clBlack;  for i := 1 to paramCount do    begin      // writeLn(i:2, '. argument: ', paramStr(i));      strA := paramStr(i).split(':');      Case strA[1] of       'drive':         s.drive:=paramStr(i);       'color':         s.color:=StringToColor(paramStr(i));       'bgcolor':         s.backgroundColor:=StringToColor(paramStr(i));       'bold': s.Bold := True;       'italic': s.Italic := True      end;    end;end;  Drawing code: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   try    for i := 0 to High(fonts) do    begin      folderName := copy(folderNameShort,3,100);      if not FontIsAvailable(fonts[i]) then        continue;      folderName := fonts[i]+#32+folderName;    for j := 0 to High(texts) do      begin        bmp.Canvas.Font.Name := fonts[i];        bmp.Canvas.Font.Size := fontSize;        bmp.Canvas.Font.Color := s.color;        if s.Italic then          bmp.Canvas.Font.Style := [fsItalic];        if s.Bold then          bmp.Canvas.Font.Style := [fsBold];        R.TopLeft := Point(0, 0);      R.BottomRight := TPoint(bmp.Canvas.TextExtent(texts[j]));        bmp.SetSize(R.Right + 2*MARGIN, R.Bottom + 2*MARGIN);        writeln(folderName);        if not DirectoryExists(folderName) then        mkdir(folderName);        if s.path<>'' then           folderName := s.path + '/' + folderName        else if s.drive<>'' then            folderName := s.drive + '/' + folderName;        fileName := IncludeTrailingPathDelimiter(folderName) + fonts[i] + ' ' + Copy(texts[j], 1, Pos(' ', texts[j]) - 1);        if s.Italic then        fileName := fileName + '_i'        if s.Bold then          fileName := fileName + '_b'        fileName := fileName + '.bmp'  Here I have a question. When I want to combine both styles bold + italic, is this like that? [fsItalic|fsBold]; I will probably add yet another nested loop to create more images with specified styles (bold, italic or bold and italic and normal). ---------------------------------------------------------------------- wp · 2023-11-03 16:26 Quote from: barracuda on November 03, 2023, 05:19:47 pmWhen I want to combine both styles bold + italic, is this like that? [fsItalic|fsBold] No. Use a comma as separator between the elements of a set. ---------------------------------------------------------------------- Handoko · 2023-11-03 17:02 Just pick one you like: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   Font.Style := [fsBold, fsItalic, fsUnderline];  Font.Style := [fsBold] + [fsItalic] + [fsUnderline]; ---------------------------------------------------------------------- TRon · 2023-11-03 17:05 Quote from: barracuda on November 03, 2023, 05:19:47 pmHere I have a question. When I want to combine both styles bold + italic, is this like that? [fsItalic|fsBold]; No, this is a set see font style. more information about set types (wp's and Handoko's first example) and set operators (Handoko's second example) ---------------------------------------------------------------------- barracuda · 2023-11-03 17:15 I think I have what I need to use: Code: [Select]type TMyStyles = (TSNormal, TSBold, TSItalic, TSBoldItalic); type   TSetInfo = record     Drive: string;     Path: string;     Color: TColor;     BackgroundColor: TColor;     Italic: Boolean;     Bold: Boolean;     Styles: Set of TMyStyles;   end; ---------------------------------------------------------------------- TRon · 2023-11-03 17:21 Quote from: barracuda on November 03, 2023, 06:15:38 pmI think I need to create my own type for styles: No, not really unless you insist or can't live without. QuoteBut this is not exactly what I need. Because this should support up to 4 styles so I can get combinations. ... And then how to find out if the style is set. I've already posted the corresponding links: Quote from: TRon on November 03, 2023, 06:05:40 pmQuote from: barracuda on November 03, 2023, 05:19:47 pmHere I have a question. When I want to combine both styles bold + italic, is this like that? [fsItalic|fsBold]; No, this is a set see font style. more information about set types (wp's and Handoko's first example) and set operators (Handoko's second example) So read about set types and set operators and try to figure out how your code should be adopted. That is imho more helpful than us adjusting your code. ---------------------------------------------------------------------- wp · 2023-11-03 17:29 Learn about the pascal type "set", e.g. https://wiki.freepascal.org/Set A set is a combination of several elements. In case of the font styles, the elements are fsBold, fsItalic, fsUnderline and fsStrikeout; they are defined in an enumeration TFontStyle (singular!) = (fsBold, fsItalic, fsUnderline, fsStrikeout). Conversely to this there is the set type TFontStyles (plural) = set of TFontStyle, it contains the some or all TFontstyle elements, or even none ("empty set"). The elements in the set are enclosed by square parenthesis: [fsBold, fsItalic] means that the font is bold and italic, [fsBold] means that it is bold only, [] is the empty set, and this means that the font is neither bold, nor italic, nor underlined, not striked-out. To check whether an element is included in the set we have the "in" operator: Assume that "FontStyle" is a set variable of the TFontStyles type. Then the expression "(fsBold in FontStyle)" is true when the font is bold, or "not (fsBold in FontStyle)" is true when the font is not bold. Checking for multiple elements can be done by several "in" calls: "(fsBold in FontStyle) or (fsItalic in FontStyle)" = font is either bold or italic, or by a "multiplication" expression: "[fsBold, fsItalic] * FontStyle <> []" - the intersection between the set [fsBold, fsItalic] and FontStyle is not empty, i.e. the FontStyle is either bold or italic. I hope you remember some elemental maths... ---------------------------------------------------------------------- barracuda · 2023-11-03 17:44 I don't remember math, I only remember the logic of binary things like 00 01 10 01 I think I need to start chatGPT to ask things. I already have some code but sure there are mistakes... I have many questions. ---------------------------------------------------------------------- TRon · 2023-11-03 17:50 Quote from: barracuda on November 03, 2023, 06:44:49 pmI don't remember math, I only remember the logic of binary things like ... I think I need to start chatGPT to ask things. I already have some code but sure there are mistakes... I have many questions. You do not need math (well you can of course  ). Sets and it operators were invented to make things simpler for us programmers: so that you do not have to use bitwise operations such as or and and to manually construct bits together yourself. What is it that you do not understand from the documentation I linked or the wiki that wp linked to ? Just declare your TSetInfo.styles field as being graphics.TFontStyles and use operators as shown by wp and Handoko (*) edit (*) that makes your TSetInfo styles recordfield and the font style property assignment compatible. ---------------------------------------------------------------------- Handoko · 2023-11-03 23:49 Quote from: barracuda on November 03, 2023, 06:15:38 pmI think I have what I need to use: Code: [Select]type TMyStyles = (TSNormal, TSBold, TSItalic, TSBoldItalic); type   TSetInfo = record     Drive: string;     Path: string;     Color: TColor;     BackgroundColor: TColor;     Italic: Boolean;     Bold: Boolean;     Styles: Set of TMyStyles;   end; No, that above is not good. This below is better: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TSetInfo = record    Path: string;    Color: TColor;    BackgroundColor: TColor;    Styles: TFontStyles  end; Reasons: - Line #3: Path. Linux does not support drive letter, your code may not work on Linux. And on Windows, drive already being included in path. So it is no need to store the drive letter separately, For example, the documentation of ExtractFilePath says drive letter is included in path: https://www.freepascal.org/docs-html/rtl/sysutils/extractfilepath.html - Line #6: Styles. You should use TFontStyles, it already includes all the styles you need. So your code should be like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TSetInfo = record    Path: string;    Color: TColor;    BackgroundColor: TColor;    Styles: TFontStyles  end; var  OutputInfo: TSetInfo; procedure TForm1.Button1Click(Sender: TObject);var  strA: array of string;  S: string;  i: integer;begin   // Set default values  OutputInfo.Path            := '';  OutputInfo.Color           := clBlack;  OutputInfo.BackgroundColor := clWhite;  OutputInfo.Styles          := [];   // Read parameters  for i := 1 to ParamCount do  begin    strA := ParamStr(i).Split(':');    S :=  strA[1];    case (strA[0]).ToLower of      'path':        OutputInfo.Path := S;      'color':        begin          if LeftStr(S, 2).ToLower <> 'cl' then S := 'cl' + S;          OutputInfo.Color := StringToColor(S);        end;      'bgcolor':        begin          if LeftStr(S, 2).ToLower <> 'cl' then S := 'cl' + S;          OutputInfo.BackgroundColor := StringToColor(S);        end;      'bold':        OutputInfo.Styles := OutputInfo.Styles + [fsBold];      'italic':        OutputInfo.Styles := OutputInfo.Styles + [fsItalic];     end;   end; end; Line #20 .. #23, it is always good to set default values at the beginning. Line #23, empty style means normal font. Line #29, store the second argument in variable "S" for easy access later. Line #30, the first argument is strA[0], because Pascal starts counting from 0. Line #30, #35, #40: ToLower, it is always good to make it lowercase (or uppercase) before comparing the strings. Line #35, #40, it is a good idea to manually add cl add the beginning of strings. ---------------------------------------------------------------------- barracuda · 2023-11-05 05:06 Handoko commented a lot on my code, so I will reply: drive and path parameters are voluntary. I use drive like a: for RAM disks on Windows XP (I use SoftDisk Perfect). I use them when I create many files for temporary reasons. I user does not want to use drive, he can use the path. Or none of them and the directories will be created. I use var s to store settings. So the TSetInfo record is meant as Settings record, not a set like set of colors. I want to use my styles not Styles: TFontStyles, but I can explain it later. I could let chatGPT to create some help to the app later. TFontStyles don't include normal or bolditalic which I want include in my program. I know this looks non-sense now. The parameter proccessing code is not so important. More important will be later code, when I will add some functions to work with graphics (I have it in my mind). I see now your goal of using the "cl" string and OutputInfo.Color := StringToColor(S); It could be one of the good solutions. But maybe it is more intuitive to any user to use color:black or color:white in parameter to make it easier to read and understand the parameters. OK, I will send you my current code to show. ChatGPT helped with some mistakes I did because I also want to remove some settings when XOR parameter is used. But again the thing with parameters is not high priority, it's just for fun. I do not work during saturday and at sunday I may be yet a little bit lazy, but I should implement the code to first create "wrapper" directories either bw (for black background and white color text images) or wb (for the white background and black color text). So I will generate more images with various fonts, and then I look forward to add some funtions which will modify the text in images a bit. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program project1; {$mode objfpc}{$H+} uses  {$IFDEF UNIX}  cthreads,  {$ENDIF}  Classes, SysUtils, CustApp,  Graphics,  LCLType, LCLIntf,  Forms, Interfaces; // required for Screen unit type TMyStyles = (TSNormal, TSBold, TSItalic, TSBoldItalic);type  TSetInfo = record    Drive: string;    Path: string;    Color: TColor;    BackgroundColor: TColor;    include_XOR: Boolean; // disables TSNormal, TSBoldItalic but must be before bold/italic specifiers    separate_NBI: Boolean; // Don't mix Bold+Italic    Italic: Boolean; // Italic or Bold can be generated together    Bold: Boolean;   // Italic or Bold can be generated together    BoldItalic: Boolean; // Generate only the BoldItalic instead Bold or Italic or Normal    Styles: Set of TMyStyles;  end;type   { TMyApplication }   TMyApplication = class(TCustomApplication)  protected    procedure DoRun; override;  private    { Private declarations }    procedure ProccessArguments();    procedure GenerateImages(fonts: array of string; texts: array of string; fontSize: Integer; folderNameShort: string);    function  FontIsAvailable(const FontName: string): Boolean;  public    constructor Create(TheOwner: TComponent); override;    destructor Destroy; override;    procedure WriteHelp; virtual;  end; { TMyApplication }var s: TSetInfo;procedure TMyApplication.DoRun;  var ErrorMsg: String;begin  // quick check parameters  ErrorMsg:=CheckOptions('h', 'help');  if ErrorMsg<>'' then begin    ShowException(Exception.Create(ErrorMsg));    Terminate;    Exit;  end;   // parse parameters  if HasOption('h', 'help') then begin    WriteHelp;    Terminate;    Exit;  end;   { add your program here }   // stop program loop  Terminate;end; constructor TMyApplication.Create(TheOwner: TComponent);const // 1st  fonts1: array[0..4] of string = ('Verdana', 'Roboto', 'Arial', 'Helvetica', 'sans-serif');  texts1: array[0..5] of string = ('Psalm 91:1', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');  fontSize1: Integer = 22;  folderName1: string = '1_22.4px'; const // 2nd  fonts2: array[0..4] of string = ('Verdana', 'Roboto', 'Arial', 'Helvetica', 'sans-serif');  texts2: array[0..5] of string = ('Text analysis', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');  fontSize2: Integer = 16;  folderName2: string = '2_16px'; const // 3rd  fonts3: array[0..2] of string = ('Arial', 'Helvetica', 'Sans-serif');  texts3: array[0..8] of string = ('Strong''s', 'Hebrew', 'English', 'Morphology', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');  fontSize3: Integer = 12;  folderName3: string = '3_12px';const // 4th  fonts4: array[0..2] of string = ('Arial', 'Helvetica', 'Sans-serif');  texts4: array[0..5] of string = ('Go to Parallel Hebrew', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');  fontSize4: Integer = 16;  folderName4: string = '4_16px';  // @TODO: Change the first number of folderName with the current Font-Familybegin  ProccessArguments();  inherited Create(TheOwner);  StopOnException:=True;  GenerateImages(fonts1, texts1, fontSize1, folderName1);  WriteLn('První dávka zkušebních obrázků (22.4px) byla uložena do složky "' + folderName1 + '".');  GenerateImages(fonts2, texts2, fontSize2, folderName2);  WriteLn('Druhá dávka zkušebních obrázků (16px) byla uložena do složky "' + folderName2 + '".');  GenerateImages(fonts3, texts3, fontSize3, folderName3);  WriteLn('Třetí dávka zkušebních obrázků (12px) byla uložena do složky "' + folderName3 + '".');  GenerateImages(fonts4, texts4, fontSize4, folderName4);  WriteLn('Čtvrtá dávka zkušebních obrázků (16px) byla uložena do složky "' + folderName4 + '".');end; destructor TMyApplication.Destroy;begin  inherited Destroy;end; procedure TMyApplication.ProccessArguments(); var i: integer; strA: Array of String;begin  s.Italic             := False;  s.Bold               := False;  s.BoldItalic         := False;  s.Color              := clWhite;  s.BackgroundColor    := clBlack;  s.Styles             := [];  s.include_XOR        := False;  s.separate_NBI       := False;  for i := 1 to paramCount do    begin      // writeLn(i:2, '. argument: ', paramStr(i));      strA := paramStr(i).split(':');      Case strA[1] of       'drive':         s.drive:=paramStr(i);       'color':         s.color:=StringToColor(paramStr(i));       'bgcolor':         s.backgroundColor:=StringToColor(paramStr(i));     'XOR': s.include_XOR:=True;       'separate': s.separate_NBI:=True;       'bold': s.Styles := s.Styles+[TSBold];       'italic': s.Styles := s.Styles+[TSItalic];       // The following option requires special code - if conditions       'BoldItalic': s.Styles := s.Styles-[TSNormal]+[TSBold]+[TSItalic];      end;    end;  if s.include_XOR then    begin      if (s.Styles = [TSBold]) OR (s.Styles = [TSItalic]) then        begin          s.Styles := s.Styles-[TSNormal];          s.Styles := s.Styles-[TSBoldItalic];      end;    end  else    begin    if s.separate_NBI then      begin      if (s.Styles = [TSBold]) OR (s.Styles = [TSItalic]) then          begin          s.Styles := s.Styles-[TSBoldItalic];          end;    end    else      begin      if (s.Styles = [TSBold]) OR (s.Styles = [TSItalic]) then          s.Styles := s.Styles-[TSNormal];      end;  end;end; procedure TMyApplication.WriteHelp;begin  { add your help code here }  writeln('Usage: ', ExeName, ' -h');end; function TMyApplication.FontIsAvailable(const FontName: string): Boolean;begin  Result := Screen.Fonts.IndexOf(FontName) >= 0;end; procedure TMyApplication.GenerateImages(fonts: array of string; texts: array of string; fontSize: Integer; folderNameShort: string);  const MARGIN = 3;  var    bmp: TBitmap;    folderName, fileName: string;    i, j: Integer;    R: TRect;begin   bmp := TBitmap.Create;  try    for i := 0 to High(fonts) do    begin      folderName := copy(folderNameShort,3,100);      if not FontIsAvailable(fonts[i]) then        continue;      folderName := fonts[i]+#32+folderName;    for j := 0 to High(texts) do      begin        bmp.Canvas.Font.Name := fonts[i];        bmp.Canvas.Font.Size := fontSize;        bmp.Canvas.Font.Color := s.color;        if s.Italic then          bmp.Canvas.Font.Style := [fsItalic];        if s.Bold then          bmp.Canvas.Font.Style := [fsBold];        R.TopLeft := Point(0, 0);      R.BottomRight := TPoint(bmp.Canvas.TextExtent(texts[j]));        bmp.SetSize(R.Right + 2*MARGIN, R.Bottom + 2*MARGIN);        writeln(folderName);        if not DirectoryExists(folderName) then        mkdir(folderName);        if s.path<>'' then           folderName := s.path + '/' + folderName        else if s.drive<>'' then            folderName := s.drive + '/' + folderName;        fileName := IncludeTrailingPathDelimiter(folderName) + fonts[i] + ' ' + Copy(texts[j], 1, Pos(' ', texts[j]) - 1);        if s.Italic then        fileName := fileName + '_i'        else if s.Bold then        fileName := fileName + '_b';        fileName := fileName + '.bmp';       bmp.Canvas.Brush.Color := s.backgroundColor;        bmp.Canvas.FillRect(0, 0, bmp.Width, bmp.Height);        bmp.Canvas.TextOut(MARGIN, MARGIN, texts[j]);        bmp.SaveToFile(fileName);      end;    end;  finally    bmp.Free;  end;end; var  Application: TMyApplication;begin  Application:=TMyApplication.Create(nil);  Application.Title:='Generate sample images with Text';  Application.Run;  Application.Free;end. ---------------------------------------------------------------------- barracuda · 2023-11-06 04:36 In short The reason why I created my own record with styles is that I don't want these styles: fsUnderline, fsStrikeout but only the bold, italic, normal and possibly combinations. And the record is not set of styles but set of settings or parameters in general. ---------------------------------------------------------------------- TRon · 2023-11-06 09:57 Quote from: barracuda on November 05, 2023, 06:06:06 amI want to use my styles not Styles: TFontStyles, but I can explain it later. I could let chatGPT to create some help to the app later. TFontStyles don't include normal or bolditalic which I want include in my program. I know this looks non-sense now. It doesn't look nonsense only now, it will always be nonsense It is not a problem perse but to me it comes across as if you still do not seem to grasp the concept of enums and sets. I already linked to the explanations and you failed to respond on my question what it is you did not understand. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program project1;type  TMyStyles = (TSNormal, TSBold, TSItalic, TSBoldItalic);  TSetInfo = record    ....    Italic: Boolean; // Italic or Bold can be generated together    Bold: Boolean;   // Italic or Bold can be generated together    BoldItalic: Boolean; // Generate only the BoldItalic instead Bold or Italic or Normal    Styles: Set of TMyStyles;  end; Normal is without any style and the moment you combine styles in your set it does not makes sense whatsoever. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Style := [tsNormal, tsBold, tsBoldItalic]; Let your own mind ponder on this one. What does that statement actually mean/do ? I honestly can't think of any situation where that is desirable. As others already pointed out: do not waste your time with GPT. The AI is really dumb, learns you bad practice and habits and does not hesitate to show you wrong answers and explanations and lies to you. It lies, not by accident but on purpose because the answer it provides needs to match the explanation but the examples it provides are about 90% flawed not to mention that the explained theory behind it is often not accurate or plain wrong. It is perfectly fine to use use things like GPT when you are experienced enough to be able to challenge the AI because you know it is writing nonsense but for beginners it is really wrong to use GPT because you are not aware you are presented nonsense. 2 cents. ---------------------------------------------------------------------- barracuda · 2023-11-06 11:13 TRon Why does not make sense? "Normal is without any style and the moment you combine styles in your set it does not makes sense whatsoever." "Let your own mind ponder on this one. What does that statement actually mean/do ? I honestly can't think of any situation where that is desirable." This is about settings for combinations of images with various fonts of font styles. So You can always want to generate Normal and Italic. Like two groups of images. Or Normal and Bold. It this explanations clearer? This is settings of the app not styles. Remember, this is about app behavior, about the algo, not about the style of one particular image. I am sure U'll better understand me when I will find more time to move forward with the app. ---------------------------------------------------------------------- wp · 2023-11-06 11:22 barracuda, your TSetInfo contains fields for Italic, Bold, BoldItalic. This is "non-sense" (sorry...). Because you can set Bold AND BoldItalic? What does this mean? Super-Bold plus a little italic? If you don't like the TFontStyles set - fine. Instead of a set introduce your enumeration TMyStyles = (TSNormal, TSBold, TSItalic, TSBoldItalic) - fine. But then do not introduce individual fields for Bold, Italic and BoldItalic in the TSetInfo, just use a Style: TMyStyle since its value contains all the information. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TMyStyles = (TSNormal, TSBold, TSItalic, TSBoldItalic);  TSetInfo = record    ....    Styles: TMyStyles;    end; ---------------------------------------------------------------------- TRon · 2023-11-06 11:30 Quote from: barracuda on November 06, 2023, 12:13:15 pm"Normal is without any style and the moment you combine styles in your set it does not makes sense whatsoever." Why does not make sense? That is why reading and understanding the documentation is important. If you do not grasp the concept then you usually end up with strange or code that does not do what you expect it to do. If your Styles field would be of type TMyStyles (instead of a set of TMyStyles) then it (somewhat) would have made sense. QuoteThis is about settings for combinations of images with various fonts of font styles. So You can always want to generate Normal and Italic. Like two groups of images. Or Normal and Bold. It this explanations clearer? This is settings of the app not styles. A enumerated type is like say a list of identifiers which each identifier being assigned a value. If you do not manipulate the value that it is assigned that (as the language allows) then each identifier gets assigned a number/value from low to high. You can retrieve the enum value by using ord(enum_identifier) so for example: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Type  TMyStyles = (TSNormal, TSBold, TSItalic, TSBoldItalic);var  x: integer;begin  x := ord(TSNormal);  // it might be that you need to use (depends on compiler settings):  // x := ord(TMyStyles.TSNormal);  A set of enumerated types treats those enumerated identifiers values as index to/for an array of bits. Meaning that if you have the enumerated type Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TMyStyles = (TSNormal, TSBold, TSItalic, TSBoldItalic); and use them in a set type: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  Styles: Set of TMyStyles; That whenever you set one of the style identifiers into the set that it sets the corresponding bit. Thus in my example: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Style := [tsNormal, tsBold, tsBoldItalic]; That means that the styles that are set are - tsNormal - tsBold - tsBoldItalic You can think of it as bits named tsNormal, tsBold and tsBoldItalic that are assigned the value 1 (or true). And they are all bits that are part of the variable Style. According to your logic you just told your style to be: normal, bold and bolditalic at the same time. How can that possibly make any sense ? a fontstyle that is normal, bold and italic at the same time ? Not only that because consider the following: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Style := [tsNormal, tsBoldItalic]; Now the style is  Normal and bolditalic at the same time. Yet the Italic style is not set. Isn't BoldItalic the same as Bold + Italic ? QuoteRemember, this is about app behavior, about the algo, not about the style of one particular image. I get that. To me it seems you are using the wrong type (set) to identify that. edit: some more clarification on enums and sets. ---------------------------------------------------------------------- barracuda · 2023-11-06 16:34 TRon This is good: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i ord(enum_identifier) I could change the name of the "styles" to something else to make more sense. Maybe StyleCombinations? Or just Combinations... Or groups .... this is too general. ---------------------------------------------------------------------- TRon · 2023-11-07 00:15 Quote from: barracuda on November 06, 2023, 05:34:33 pmTRon This is good: No actually it is not (meaning: my bad). I think I need to apologize to you barracuda (which I do right here) because it seems like it is starting to daunt on me what you (actually) meant when you wrote: Quote from: barracuda on November 06, 2023, 12:13:15 pmThis is about settings for combinations of images with various fonts of font styles. So You can always want to generate Normal and Italic. Like two groups of images. Or Normal and Bold. It this explanations clearer? This is settings of the app not styles. Remember, this is about app behavior, about the algo, not about the style of one particular image. I am sure U'll better understand me when I will find more time to move forward with the app. Yes on the last sentence because what /I currently think/ you are trying to do is that the user can provide bold and italic on the command line /but/ you want the user to be able to select to either combine these two styles or treat them as two independent/individual styles. In that case your declaration and use for your Styles field is correct (it is just poorly named which seem to cause the confusion on at least my part). The reason I can not tell for sure is because I do not understand the current implementation of the command line parsing. Note that I currently believe that your argument parsing has a flaw in it but I want to make sure not to make the same mistake twice. Therefor I would like to ask if you could elaborate on what the user needs to pass as arguments, how these combine (or are suppose to be combined) and what you expect the output to be ? Basically I ask for a complete and understandable implementation for the WriteHelp method so that it is clear from the help how the arguments work or are suppose to work. In case I am right about our misunderstanding then I truly apologize for that. ---------------------------------------------------------------------- barracuda · 2023-11-07 14:38 I tried to created help using chatGPT. I renamed the types enum and record. Please note, the blog is to different old project which I will never finish. I just used it to create new page to paste the text from chatGPT and published it there. https://texture-edit.blogspot.com/2023/11/image-groups-of-text- generator-in.html I discussed with it also the new suggestions regarding the implementation. I AM NOT SURE IF THE LLM Really understands the problem, but this is the code it suggests: https://texture-edit.blogspot.com/2023/11/groups-of-images-generator- chatgpt.html I am not sure if the s.groups actually should not be an array instead of set? If you see there any logical problems, please tell me to correct. Thank you. My current code (this is the input which I passed to chatGPT to produce help and suggestions): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i // Program to generate combinations of images// With text of various fonts and styles// The text images completly depends on user settingsprogram project1; {$mode objfpc}{$H+} uses  {$IFDEF UNIX}  cthreads,  {$ENDIF}  Classes, SysUtils, CustApp,  Graphics,  LCLType, LCLIntf,  Forms, Interfaces; // required for Screen unit // enum and record for "settings" or configuration of the app// e.e. TMyImgGrpPreferences & TSettingstype TMyImgGrpPreferences = (TSNormal, TSBold, TSItalic, TSBoldItalic);type  TSettings = record    Drive: string;    Path: string;    Color: TColor;    BackgroundColor: TColor;    include_XOR: Boolean; // disables TSNormal, TSBoldItalic but must be before bold/italic specifiers    separate_NBI: Boolean; // Don't mix Bold+Italic    Italic: Boolean; // Italic or Bold can be generated together    Bold: Boolean;   // Italic or Bold can be generated together    BoldItalic: Boolean; // Generate only the BoldItalic instead Bold or Italic or Normal    Groups: Set of TMyImgGrpPreferences;  end;type   { TMyApplication }   TMyApplication = class(TCustomApplication)  protected    procedure DoRun; override;  private    { Private declarations }    procedure ProccessArguments();    procedure GenerateImages(fonts: array of string; texts: array of string; fontSize: Integer; folderNameShort: string);    function  FontIsAvailable(const FontName: string): Boolean;  public    constructor Create(TheOwner: TComponent); override;    destructor Destroy; override;    procedure WriteHelp; virtual;  end; { TMyApplication }var s: TSettings;procedure TMyApplication.DoRun;  var ErrorMsg: String;begin  // quick check parameters  ErrorMsg:=CheckOptions('h', 'help');  if ErrorMsg<>'' then begin    ShowException(Exception.Create(ErrorMsg));    Terminate;    Exit;  end;   // parse parameters  if HasOption('h', 'help') then begin    WriteHelp;    Terminate;    Exit;  end;   { add your program here }   // stop program loop  Terminate;end; constructor TMyApplication.Create(TheOwner: TComponent);const // 1st  fonts1: array[0..4] of string = ('Verdana', 'Roboto', 'Arial', 'Helvetica', 'sans-serif');  texts1: array[0..5] of string = ('Psalm 91:1', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');  fontSize1: Integer = 22;  folderName1: string = '1_22.4px'; const // 2nd  fonts2: array[0..4] of string = ('Verdana', 'Roboto', 'Arial', 'Helvetica', 'sans-serif');  texts2: array[0..5] of string = ('Text analysis', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');  fontSize2: Integer = 16;  folderName2: string = '2_16px'; const // 3rd  fonts3: array[0..2] of string = ('Arial', 'Helvetica', 'Sans-serif');  texts3: array[0..8] of string = ('Strong''s', 'Hebrew', 'English', 'Morphology', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');  fontSize3: Integer = 12;  folderName3: string = '3_12px';const // 4th  fonts4: array[0..2] of string = ('Arial', 'Helvetica', 'Sans-serif');  texts4: array[0..5] of string = ('Go to Parallel Hebrew', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');  fontSize4: Integer = 16;  folderName4: string = '4_16px';  // @TODO: Change the first number of folderName with the current Font-Familybegin  ProccessArguments();  inherited Create(TheOwner);  StopOnException:=True;  GenerateImages(fonts1, texts1, fontSize1, folderName1);  WriteLn('První dávka zkušebních obrázků (22.4px) byla uložena do složky "' + folderName1 + '".');  GenerateImages(fonts2, texts2, fontSize2, folderName2);  WriteLn('Druhá dávka zkušebních obrázků (16px) byla uložena do složky "' + folderName2 + '".');  GenerateImages(fonts3, texts3, fontSize3, folderName3);  WriteLn('Třetí dávka zkušebních obrázků (12px) byla uložena do složky "' + folderName3 + '".');  GenerateImages(fonts4, texts4, fontSize4, folderName4);  WriteLn('Čtvrtá dávka zkušebních obrázků (16px) byla uložena do složky "' + folderName4 + '".');end; destructor TMyApplication.Destroy;begin  inherited Destroy;end; procedure TMyApplication.ProccessArguments(); var i: integer; strA: Array of String;begin  s.Italic             := False;  s.Bold               := False;  s.BoldItalic         := False;  s.Color              := clWhite;  s.BackgroundColor    := clBlack;  s.Groups             := [];  s.include_XOR        := False;  s.separate_NBI       := False;  for i := 1 to paramCount do    begin      // writeLn(i:2, '. argument: ', paramStr(i));      strA := paramStr(i).split(':');      Case strA[1] of       'drive':         s.drive:=paramStr(i);       'color':         s.color:=StringToColor(paramStr(i));       'bgcolor':         s.backgroundColor:=StringToColor(paramStr(i));     'XOR': s.include_XOR:=True;       'separate': s.separate_NBI:=True;       'bold': s.Groups := s.Groups+[TSBold];       'italic': s.Groups := s.Groups+[TSItalic];       // The following option requires special code - if conditions       'BoldItalic': s.Groups := s.Groups-[TSNormal]+[TSBold]+[TSItalic];      end;    end;  if s.include_XOR then    begin      if (s.Groups = [TSBold]) OR (s.Groups = [TSItalic]) then        begin          s.Groups := s.Groups-[TSNormal];          s.Groups := s.Groups-[TSBoldItalic];      end;    end  else    begin    if s.separate_NBI then      begin      if (s.Groups = [TSBold]) OR (s.Groups = [TSItalic]) then          begin          s.Groups := s.Groups-[TSBoldItalic];          end;    end    else      begin      if (s.Groups = [TSBold]) OR (s.Groups = [TSItalic]) then          s.Groups := s.Groups-[TSNormal];      end;  end;end; procedure TMyApplication.WriteHelp;begin  { add your help code here }  writeln('Usage: ', ExeName, ' -h');end; function TMyApplication.FontIsAvailable(const FontName: string): Boolean;begin  Result := Screen.Fonts.IndexOf(FontName) >= 0;end; procedure TMyApplication.GenerateImages(fonts: array of string; texts: array of string; fontSize: Integer; folderNameShort: string);  const MARGIN = 3;  var    bmp: TBitmap;    folderName, fileName: string;    i, j: Integer;    R: TRect;begin   bmp := TBitmap.Create;  try    for i := 0 to High(fonts) do    begin      folderName := copy(folderNameShort,3,100);      if not FontIsAvailable(fonts[i]) then        continue;      folderName := fonts[i]+#32+folderName;    for j := 0 to High(texts) do      begin        bmp.Canvas.Font.Name := fonts[i];        bmp.Canvas.Font.Size := fontSize;        bmp.Canvas.Font.Color := s.color;        if s.Italic then          bmp.Canvas.Font.Style := [fsItalic];        if s.Bold then          bmp.Canvas.Font.Style := [fsBold];        R.TopLeft := Point(0, 0);      R.BottomRight := TPoint(bmp.Canvas.TextExtent(texts[j]));        bmp.SetSize(R.Right + 2*MARGIN, R.Bottom + 2*MARGIN);        writeln(folderName);        if not DirectoryExists(folderName) then        mkdir(folderName);        if s.path<>'' then           folderName := s.path + '/' + folderName        else if s.drive<>'' then            folderName := s.drive + '/' + folderName;        fileName := IncludeTrailingPathDelimiter(folderName) + fonts[i] + ' ' + Copy(texts[j], 1, Pos(' ', texts[j]) - 1);        if s.Italic then        fileName := fileName + '_i'        else if s.Bold then        fileName := fileName + '_b';        fileName := fileName + '.bmp';       bmp.Canvas.Brush.Color := s.backgroundColor;        bmp.Canvas.FillRect(0, 0, bmp.Width, bmp.Height);        bmp.Canvas.TextOut(MARGIN, MARGIN, texts[j]);        bmp.SaveToFile(fileName);      end;    end;  finally    bmp.Free;  end;end; var  Application: TMyApplication;begin  Application:=TMyApplication.Create(nil);  Application.Title:='Generate sample images with Text';  Application.Run;  Application.Free;end. ---------------------------------------------------------------------- TRon · 2023-11-07 15:36 Quote from: barracuda on November 07, 2023, 03:38:55 pmhttps://texture-edit.blogspot.com/2023/11/image-groups-of-text- generator-in.html Ok, thank you for the link. That clarifies a few things. Do note that your current code isn't actually processing the arguments as is written in that link (but that is another topic). Btw the arguments as described: Is that what the AI advised you to use or is that actually how you wish to define and supply the arguments to your program  ? I ask because as far as I am able to tell, the following list is all the different output that the program is (ever) able to produce: - images with normal text (no font style applied) - images with Bold text (bold font style applied) - images with Italic text (Italic font style applied) - images with Bold+Italic text (bold and Italic font style applied) And you want your program to output images with any of these possible combinations on a single run of your program ? Thus f.e. this imaginary solution/approach: MyApp -color:clWhite -bgcolor:clBlack -fontstyles:normal (could be default when no fontstyle is provided at all) MyApp -color:clWhite -bgcolor:clBlack -fontstyles:bold MyApp -color:clWhite -bgcolor:clBlack -fontstyles:italic MyApp -color:clWhite -bgcolor:clBlack -fontstyles:bold+italic MyApp -color:clWhite -bgcolor:clBlack -fontstyles:normal,bold MyApp -color:clWhite -bgcolor:clBlack -fontstyles:normal,bold,italic MyApp -color:clWhite -bgcolor:clBlack -fontstyles:normal,italic MyApp -color:clWhite -bgcolor:clBlack -fontstyles:normal,italic,bold+italic MyApp -color:clWhite -bgcolor:clBlack -fontstyles:normal,bold,bold+italic MyApp -color:clWhite -bgcolor:clBlack -fontstyles:normal,bold,italic,bold+italic Because if that is the case then the AI is over complicating things for no gain at all. Note that for me it is even impossible to create such a list based on the provided information regarding the -xor, -xnor and -separate arguments. Do note that if you want to restrict the different font styles used for the images output based on another provided fontstyle then the example code as you showed has a point (to some degree) but I fail to see/notice it right now. Currently I do not understand why the document from your link advises to use -XOR, -XNOR and -separate arguments. ---------------------------------------------------------------------- barracuda · 2023-11-07 19:30 The LLM suggested to use parameters in the way where the "-" is present before the parameters. Maybe it would have a sense to do it like that as it suggested. The "normal" should be generated always except one case... But now I see mistake in the help regarding -XOR and -XNOR parameters. "XOR: Generates groups of images with "bold" group of styles and "italic" group of styles (e.i two groups of images). -XNOR: Generates groups of images with "normal" AND "bold italic" (e.i two groups of images) There is a lot of combinations but when XOR is used, there is not "normal" group of styles. I've corrected the help article with name "Image Groups of text Generator in FreePascal" ---------------------------------------------------------------------- TRon · 2023-11-07 23:16 Quote from: barracuda on November 07, 2023, 08:30:31 pmThe LLM suggested to use parameters in the way where the "-" is present before the parameters. Maybe it would have a sense to do it like that as it suggested. It is not really a problem how you wish to provide the arguments on the command line. The way to implement it as suggested by the document is not 'normal' for Free Pascal but in practice it does not really matter as Free Pascal allows you to implement/support any argument handling scheme as you see fit. Depending on which scheme is implemented there will usually always be some form of ambiguity but imho the scheme currently suggested is to complicated for what you actually want to achieve (to my knowledge). QuoteThe "normal" should be generated always except one case... But now I see mistake in the help regarding -XOR and -XNOR parameters. Ok, that makes sense then. Thank you for correcting. But, let's forget about LLM and different styles for a moment and have a look at your ProccessArguments method. There are some things in there that does not make sense. - if you truly wish to use the minus sign to indicate an option on the command line then you should check against it in your code. The current code-base that you showed does not do that. - The current code-base splits up the Paramstr into several parts based on the colon character. That means that the actual name of the argument option is located in strA[0] (the first extracted split) and the value of that option is located in strA[1] (the second extracted split). The current-code base handles that incorrect. So try to focus on that first before worrying about font-styles and what more. In that regards also note the post from user Handoko who already showed you what was wrong with the original code-base that you use(d). fwiw: I am almost 100% certain that you can't teach the LLM that it has messed up and let it fix the mistakes it made. By the time you have constructed/used the correct sentences to describe the problem in order to make the AI fix its errors you also have figured out how to do it in code yourself. That is the problem with using AI. It only works when you are very specific with lots of details about what it is that you want it to produce and even when you provide all that information it still messes up because it approaches the issue from the wrong point of view (even if you made the correct point of view very clear). ---------------------------------------------------------------------- barracuda · 2023-11-08 10:53 Regarding LLM U R absolutely right. It is slow discovering of wrong and better and finally find out that it is eigher useless or it does the job... after spending two hours of chatting. There is another mistake... myapp -color:clRed -bgcolor:clBlack -italic instead of myapp -color:black -bgcolor:white -italic But that are small things. Today I have more time so I could start to modify the code. ---------------------------------------------------------------------- barracuda · 2023-11-08 21:41 This is all I prepared today: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i       strA := paramStr(i).split('=');      cStr := strA[1];      Case strA[0] of       '-drive':         s.drive:=cStr;       '-path':         s.path:=cStr;       '-color':         if (Length(cStr) > 0) and (cStr[1] = '#') then           s.Color := StringToColor(cStr)         else           begin             cStr:='cl'+AnsiUpperCase(cStr[1])+Copy(cStr, 2, Length(cStr) - 1);           s.Color := StringToColor(cStr);           end;       '-bgcolor':       if (Length(cStr) > 0) and (cStr[1] = '#') then         s.backgroundColor := StringToColor(cStr)         else         begin           cStr:='cl'+AnsiUpperCase(cStr[1])+Copy(cStr, 2, Length(cStr) - 1);           s.backgroundColor := StringToColor(cStr);         end; ---------------------------------------------------------------------- TRon · 2023-11-09 00:39 Quote from: barracuda on November 08, 2023, 10:41:57 pmThis is all I prepared today: Any which way you look at it it is progress as it also seem to be heading into the right direction  Sorry in advance for the upcoming wall of text. Ok, so now that you have added support for handling the more simple command-line arguments, let's have a closer look at the help text that you provided. This is the presented usage: Code: [Select]myapp [-h] [-drive:] [-path:] [-color:] [-bgcolor:] [-XOR] [-XNOR] [-separate] [-bold] [-italic] [-BoldItalic] It doesn't really matter what all the options do or are suppose to do at this point because, at least for/to me, I am already w(e)ary when I noticed several switches being mentioned and which seem to be related to selecting styles. Especially the XOR and XNOR switches initially seem to indicate that they exist to let the program be able for the user to select between opposite behaviour (which is bad thing to do for command-line switches). To further investigate I want to draw your attention on a particular example: Quote4. myapp -drive:E -XOR -XNOR Notes: - The "XOR" parameter generates groups of images with combinations of "bold" or "italic" styles. - The "XNOR" parameter generates groups of images with combinations of "normal" or "bold" and "normal" or "italic" styles. - If you want to generate images with combinations of multiple styles, use multiple command-line parameters, for example, -bold -italic. This way, you can generate images with different fonts and styles according to your preferences. Unless I completely misunderstood what all the argument options are suppose to do then the last line in that quote does more or less say it all. "generate images with different fonts and styles according to your preferences" Because it makes me wonder why there is made the suggestion to use all kind of switches and options to select your preferences and which have all kinds of conditions attached (that frankly speaking is difficult to understand for/to the casual user) ? Because what is the quoted item 4 actually telling ? - use XOR to   1) create images with bold font style and   2) create images with italic font style - use XNOR to  1) create images with normal font style and  2) create images with bold font style  or  1) create images with normal font style and  2) create images with Italic font style And if you want to deviate from that then you can also use the additional -bold and -italic commandline switches. As mentioned before: That is way too complicated (for such a simple thing) and imho can only lead to confusion for/to the (end)user (no matter if it is only a private tool that only you yourself are using). I like to keep things simple wherever/whenever possible. The goal: create images with different font styles How many fontstyles or combination of fontstyles (groups) actually are there ? - normal (= no font style applied) - italic - bold - bold+italic combined These can be divided into 3 different style groups (as you did in your current code-base): - No font style is applied at all (normal) - a single font style is applied (italic or bold) - a combination of font styles is applied (italic and bold) It also seems that you want for the program to divide the fontstyles that need to be applied to be divided into something like these mentioned style groups (see XOR / XNOR switches). Note that I conveniently ignored the other implications that are/seem to be (indirectly) implied by the help when using those switches. In other words you want the user to be able to select/choose which font style or combination of font styles is applied to the font that is used for the text of the images. So, /I/, the enduser thinks: - text can be written with a normal fontstyle, and - text can be written with a italic fontstyle, and - text can written with a bold fontstyle, and - text can be written with a italic + bold fontstyle or be able to choose between any of these different options to provide me with images with the text style that the program supports. Thus (virtual example) that results into the following possible commands: MyApp -normal MyApp -normal -bold_and_italic MyApp -normal -bold MyApp -normal -bold -bold_and_italic MyApp -normal -italic MyApp -normal -italic -bold_and_italic MyApp -normal -italic -bold MyApp -normal -italic -bold -bold_and_italic MyApp -bold_and_italic MyApp -bold MyApp -bold -bold_and_italic MyApp -italic MyApp -italic -bold_and_italic MyApp -italic -bold MyApp -italic -bold -bold_and_italic In one of my previous examples I used the virtual example construct Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i MyApp -fontstyles:normal,bold etc .... because that looked a lot nicer to me in comparison to all these different individual switches. But as said the actual implementation does not matter/differ much and depends more on your (personal) preferences. Point is that when you would implement it in such a way as shown then you can let the user decide which exact font style is applied to the text of the images. So far I have understood that the program is suppose to output several images divided into 4 categories, each category having its own number of text-items that is drawn to individual images using different fonts(names) (configurable by category) using the category's defined fontsize. Depending on what the user provides as arguments on the commandline each of these text-items need to be written with the different fonts with the category's fontsize using these commandline provided fontstyle(groups). Meaning that if the commandline would be: Myapp -fontstyles:normal,bold that each individual textitem belonging to a category is drawn to 2 separate images: one image that uses the normal fontstyle (no style applied at all) and second image using the bold fontstyle and to do that for each fonts that is defined/present for that category. Or to put it into other words: a whole lot of images  If you would implement it as suggested in my virtual examples then you can get rid of the following options/switches: -XOR -XNOR -separate ... and if you also would opt for using a single switch to let the user select which different kind of style groups need to be applied you could also get rid of the options/switches: -bold -italic -BoldItalic Which imho would result in that handling these switches is easier to implement, easier to understand the code and therefor also easier to maintain in your code-base. Now, let's say that you are still inclined to have the program to apply the indirect implied behaviour as currently implemented for the XOR and XNOR switches then you could address that either by detecting that applying different fontstyles from a particular fontstyle group is conflicting with another style (or style group) and let your program fault and/or inform the user in case that situation occurs or you can apply the same indirect implied behaviour as it is currently implemented (but in that case and imho it would be easier to implement and understand). I would personally opt for letting the program emit a message about the conflict and let the program fault because when the emitted message is crisp and clear it is easy enough for the enduser to correct. But your might perhaps have a different idea about that. Of course my reply is based on what I currently know about the program. It might be that the current code-base that you showed is just the start/beginning of a much more complicated program that requires even more features and in which case it is perfectly possible that the suggestions that I opted do not make any sense at all (anymore). Or perhaps I interpreted things wrong so that it makes no sense at all what I just wrote. It is difficult for me to tell at this point so I'll let you be the judge of that BTW: if you would opt for the -fontstyles=stylegroup1,stylegroup2 (or something to that extend) then it is easy to implement in your current parameter handling code that you just showed. For that you can first check for the existence for the -fontstyles option name (or whatever you prefer to name it) then store the option value in a string tat you can split again using the comma as split character so that you end up with an array of applied stylenames which can then be translated/converted to your record field named Groups of your settings record. The used names, characters used for splitting etc is just unimportant details and can be changed to whatever you want it to be. BTW2: Another thing I noticed in the help text for the command-line options: There is an argument option for Drive and another one for path. I do not know the reason for the separation of these options (the reason is also not clarified by your current used code-base) but if you would change the character to split the option name from the option value to not being a colon (but for example use the equal sign as it is normally used for such options that have a value) then it is possible to combine these two separate options into one. If you still need to separate between these two (path vs driveletter) then you can always do that later in your code. ---------------------------------------------------------------------- barracuda · 2023-11-09 10:52 This is a lot text to read. For now just use of -XOR -XNOR does not make any sense ---------------------------------------------------------------------- TRon · 2023-11-09 11:27 Quote from: barracuda on November 09, 2023, 11:52:09 amThis is a lot text to read. No problem barracuda. I am/was assuming that English isn't your native language so take your time. BTW: if the XOR/XNOR options doesn't make sense right now at this point but later on they will then it would probably help to know what future features (if any) does make sense for these options to use. Right now I have a simple testcase (that has discarded the options that I mentioned in my previous post (for which I opted that these could be removed). ---------------------------------------------------------------------- barracuda · 2023-11-10 12:19 Use of Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program.exe -XOR -XNOR doesn't make sense because NOR is opposite to OR or XNOR is opposite to XOR. Let's say we have two pairs of shoes red and black. Any you specify XNOR which means that you want black boots and red boots. If you say you want OR, you get one boot red and the second black  . But if you say you want to buy XOR and NOR boots, then you will obtain nothing... Because you excluded both options... The problem is that you did not quote the manual or did you qoute from different place than me? "-XOR: Generates groups of images with "bold" styles AND groups of images with "italic" styles (i.e two groups of images with two styles). -XNOR: Generates groups of images with "normal" styles AND "bold+italic" styles (i.e two groups of images with two styles)." So you missunderstand when you wrote: "- use XNOR to  1) create images with normal font style and  2) create images with bold font style  or  1) create images with normal font style and  2) create images with Italic font style " This is very different. See the mistakes: "- use XNOR to  1) create images with normal font style and  2) create images with bold font style  or  1) create images with normal font style and  2) create images with Italic font style " Should be like that: This is very different. See the mistakes: "- use XNOR to  1) create groups of images with normal font style and  2) create groups of images with bold italic font style " I use -drive=letter: only for RAM disk, but this is my preference. It is easier to change the char between A: and B: So for example when I want to compare files in A: I compare with B: not naming folders. The word path suggests that you want to specify directory... As for linux path makes sense than drive. ---------------------------------------------------------------------- TRon · 2023-11-10 13:44 Quote from: barracuda on November 10, 2023, 01:19:49 pmbecause NOR is opposite to OR or XNOR is opposite to NOR. Yeah, I figured as much but the quote I took from the help seem to suggest otherwise. QuoteLet's say we have two pairs of shoes red and black. Any you specify XNOR which means that you want black boots and red boots. If you say you want OR, you get one boot red and the second black  . OK, I will try and stick (at least a bit) with that analogy  QuoteBut if you say you want to buy XOR and NOR boots, then you will obtain nothing... Because you excluded both options... Exactly. Quote.... later on in your post you wrote some examples to explain the difference between nor and xor and how it is intended to be used. You also pointed out the mistakes of my explanation(s)/interpretation(s) for the NOR / XNOR commandline switches. Thank you for mentioning my mistakes and the corrections that you made. But, the point I was actually trying to make is that when you are at the store looking at those boots the 'normal' buyer will never ask the sales representative: let me buy these boots xor (or xnor) the other pair of boots. You'll probably end up asking for the left and right shoe for the black pair and the left shoe of the red pair of boots (or whatever combination seems more appealing for you as buyer). I don't know, maybe math professors will ask for a nor/xnor selection of boots. So, it is imho easier that by default the program does nothing and let the enduser choose/select what pair of boots or which shoe(s) (left or right and/or whatever model(s)) the users wants to buy. If somehow you want that a combination of shoes (or models or whatever other selection or combination of that) is inadmissible, then inform the user that it is an illegal combination and let the user make another choice. But as said, that is how I would prefer (and try to suggest to you) to implement. I do not have the same insights about and/or ambitions for your program that you have so I can only respond to what I (currently) know and what you (can) share. QuoteIf you don't mind, I will modify/update this post while writting. That is ok. I have taken the liberty to put your replies into quotes. QuoteThe problem is that you did not quote the manual or did you qoute from different place than me? The only source I have is what you provided for us: https://texture- edit.blogspot.com/2023/11/image-groups-of-text-generator-in.html And the 4th example mentions the combination. It is usually a bad idea to rely on 3th party websites to store relevant information (the site itself or the content have a habit of disappearing or changing content over time and in which case it renders the discussion here pointless). That is why I quoted the 3th party website's text so that it becomes clear (also for other readers) to what I am responding to. There is already a lot of context about your program missing from this thread so the discussion is probably already a bit weird for the casual reader or those that only skim through. QuoteI use -drive=letter: only for RAM disk, but this is my preference. Preference says it all. No problem. I merely wanted to point out that there is virtually no difference between providing a drive letter and/or (relative) path as they can be treated the same in code (and if required you can extract the different parts quite easily). QuoteThe word path suggests that you want to specify directory... As for linux path makes sense than drive. I had choosen to name the option "dir" but I see your point. You could always opt to name it location, folder, drawer, storepath or whatever is more fitting. ---------------------------------------------------------------------- barracuda · 2023-11-10 13:52 Oh my gosh I mixed words...  Instead XOR i wrote OR. Opposite to XOR is XNOR. Now it is correct. In the last manual page, the LLM generated wrong description. This is corrected: Code: [Select]if s.include_XOR then begin   s.Groups := [TSBold, TSItalic]; // Only "bold" group and "italic" group end else if s.include_XNOR then begin   s.Groups := [TSNormal, TSBoldItalic]; // Only "normal" group and "bolditalic" group end Because it printed "combinations" instead "group" and that was misleading. Sorry. I've just checked the code at the implementation manual part 2, the LLM made it wrong. 2. Modify ... must be rewritten (right now it is wrong). It messed it up. So I think I need to create new function which will be nested. Now I separate the code to control block and generation block. The generation block must be inside the case blocks of control block. New variable for the nested loop: GroupStyle Code: [Select]var     GroupStyle: TMyImgGrpPreferences; The main loop: Code: [Select]  try     for i := 0 to High(fonts) do     begin       folderName := copy(folderNameShort,3,100);       if not FontIsAvailable(fonts[i]) then         continue;       folderName := fonts[i]+#32+folderName;       for j := 0 to High(texts) do       begin         bmp.Canvas.Font.Name := fonts[i];         bmp.Canvas.Font.Size := fontSize;         bmp.Canvas.Font.Color := s.color;         // BLOCK OF CONTROL:         for GroupStyle in s.Groups do         begin           // Set the font style for the group to be processed           case GroupStyle of             TSNormal: bmp.Canvas.Font.Style := [];             TSBold: bmp.Canvas.Font.Style := [fsBold];             TSItalic: bmp.Canvas.Font.Style := [fsItalic];             TSBoldItalic: bmp.Canvas.Font.Style := [fsBold, fsItalic];           end;         end;         // BLOCK OF GENERATION:         R.TopLeft := Point(0, 0);         R.BottomRight := TPoint(bmp.Canvas.TextExtent(texts[j]));         bmp.SetSize(R.Right + 2*MARGIN, R.Bottom + 2*MARGIN);         writeln(folderName);         if not DirectoryExists(folderName) then           mkdir(folderName);         if s.path<>'' then            folderName := s.path + '/' + folderName         else if s.drive<>'' then             folderName := s.drive + '/' + folderName;         fileName := IncludeTrailingPathDelimiter(folderName) + fonts[i] + ' ' + Copy(texts[j], 1, Pos(' ', texts[j]) - 1);         if s.Italic then           fileName := fileName + '_i'         else if s.Bold then           fileName := fileName + '_b';         fileName := fileName + '.bmp';         bmp.Canvas.Brush.Color := s.backgroundColor;         bmp.Canvas.FillRect(0, 0, bmp.Width, bmp.Height);         bmp.Canvas.TextOut(MARGIN, MARGIN, texts[j]);         bmp.SaveToFile(fileName);       end;     end;   finally     bmp.Free;   end; PROCESSING ARGUMENTS: Hope all things make sense now. Code: [Select]       '-bold':          begin            s.Groups := s.Groups+[TSBold];            if s.Italic then              s.Groups := s.Groups+[TSBoldItalic];          end;        '-italic':          begin            s.Groups := s.Groups+[TSItalic];            if s.Bold then              s.Groups := s.Groups+[TSBoldItalic];          end; ---------------------------------------------------------------------- TRon · 2023-11-11 00:41 Quote from: barracuda on November 10, 2023, 02:52:33 pmOh my gosh I mixed words...  Instead XOR i wrote OR. Opposite to XOR is XNOR. Now it is correct. Thank you. QuoteIn the last manual page, the LLM generated wrong description. This is corrected: Again, thank you. QuoteBecause it printed "combinations" instead "group" and that was misleading. Sorry. I've just checked the code at the implementation manual part 2, the LLM made it wrong. 2. Modify ... must be rewritten (right now it is wrong). It messed it up. No need to be sorry. As said, I/we can only work with what you shared and what was shared did not make any sense (to me). Now, with your corrections, it is becoming more and more clear QuoteSo I think I need to create new function which will be nested. Now I separate the code to control block and generation block. The generation block must be inside the case blocks of control block. Yes, indeed. Funny to mention is that you seem to put your stylegroup loop last (most inner loop) while for my test I placed it first (most outer loop). In practice it does not matter much whether you process the stylegroups first or last (or even in between if you have the urge to do so). The only difference is the order in which the pictures are saved. fwiw: now even the nor and xnor switches are starting to make sense though I still have the opinion that the switches can be used in an ambiguous manner so I opted to let the user decide. Good to see that you are making progress ! Thank you for sharing. ---------------------------------------------------------------------- barracuda · 2023-11-11 10:12 Quick response for now. The order of images. Highest priority is for Font-family, size etc. The style Normal, Italic, is less priority. I will create a structure to pass the arguments, bitmap pointer etc to the inner loop function. Otherwise there would be too long list of arguments. ---------------------------------------------------------------------- TRon · 2023-11-11 15:38 Quote from: barracuda on November 11, 2023, 11:12:06 amQuick response for now. The order of images. Highest priority is for Font-family, size etc. The style Normal, Italic, is less priority. I will create a structure to pass the arguments, bitmap pointer etc to the inner loop function. Because my testproject lets the user decide which styles to apply, I implemented it as most outer loop because in case there is no style(group) provided then the testproject will not generate any image at all (*). Thus the loop construct itself decided if the loop will run or not (though I could have checked for that beforehand as well, I'll adjust my testcode). QuoteOtherwise there would be too long list of arguments. Oh, I only use the arguments as provided by your example code. - FontNames, the names of all the fonts that need to be applied - TextLines, the individual text lines that are written - fontSize, the size of the font to use - FolderName, the special subfolder name that was supplied for this category. The rest of the required information is either stored inside the settings record and/or can be constructed from that information. But perhaps you have extended your implementation and I am not aware there are other requirements that come into play. (*) My testproject actually does not generate any image at all (as you got that already covered correctly) and only emits the information it will use to generate the image: Code: [Select]  Filename         = /somewhere/over/the/rainbow/1_22.4px/font1 two.bmp   Background Color = white   Text             = two   Font.Name        = font1   Font.Size        = 22   Font.Color       = black   Font.Style       = [] ---------------------------------------------------------------------- barracuda · 2023-11-11 20:24 I've just finished the implementation https://texture-edit.blogspot.com/2023/11/groups-of-images-generator- chatgpt.html I will implement three new parameters: -margin, -text , -characters This is what I will probably do tomorow. And I will check how it runs... Today I just compiled without errors. I will read your answer tomorow. Thank you. Also thank you for your politeness. Code: [Select]// Program to generate combinations of images // With text of various fonts and styles // The text images completly depends on user settings // Usual use of this program is to genarate at least two group of images, which one uses Normal style. program project1; {$mode objfpc}{$H+} uses   {$IFDEF UNIX}   cthreads,   {$ENDIF}   Classes, SysUtils, CustApp,   Graphics,   LCLType, LCLIntf,   Forms, Interfaces; // required for Screen unit // enum and record for "settings" or configuration of the app // i.e. TMyImgGrpPreferences & TSettings type TMyImgGrpPreferences = (TSNormal, TSBold, TSItalic, TSBoldItalic); type   TSettings = record     Drive: string;     Path: string;     Color: TColor;     BackgroundColor: TColor;     include_XOR: Boolean; // disables TSNormal, TSBoldItalic but must be before bold/italic specifiers     include_XNOR: Boolean; // disables TSBold, TSItalic, enables TSNormal, TSBoldItalic     separate_NBI: Boolean; // Don't mix Bold+Italic     Italic: Boolean; // Italic or Bold can be generated together     Bold: Boolean;   // Italic or Bold can be generated together     BoldItalic: Boolean; // Generate only the BoldItalic instead Bold or Italic or Normal     JustNormal: Boolean; // This is just for unusual parameters like program.exe -XOR -XNOR     JustBoldItalic: Boolean; // This is just for unusual parameters like program.exe -XNOR -XOR     Groups: Set of TMyImgGrpPreferences;     Margin: Byte; // -margin Margin around text     Text: String; // -text When you want to test just one text string, not an array     FontFamily: String; // For use with -text     FontSize: Byte; // For use with -text     Characters: String; // -characters When you want to create images of characters as defined here   end; type  TGenerateImageParams = record     GroupStyles: TMyImgGrpPreferences;     bmp: TBitmap;     folderName, font, text: string;     i, j: Integer;     R: TRect;   end;   { TMyApplication }   TMyApplication = class(TCustomApplication)   protected     procedure DoRun; override;   private     { Private declarations }     procedure ProccessArguments();     procedure GenerateImages(fonts: array of string; texts: array of string; fontSize: Integer; folderNameShort: string);     procedure GenerateImageWithStyle(p: TGenerateImageParams; fs: TFontStyles);     function  FontIsAvailable(const FontName: string): Boolean;   public     constructor Create(TheOwner: TComponent); override;     destructor Destroy; override;     procedure WriteHelp; virtual;   end; { TMyApplication } var s: TSettings; procedure TMyApplication.DoRun;   var ErrorMsg: String; begin   // quick check parameters   ErrorMsg:=CheckOptions('h', 'help');   if ErrorMsg<>'' then begin     ShowException(Exception.Create(ErrorMsg));     Terminate;     Exit;   end;   // parse parameters   if HasOption('h', 'help') then begin     WriteHelp;     Terminate;     Exit;   end;   { add your program here }   // stop program loop   Terminate; end; constructor TMyApplication.Create(TheOwner: TComponent); const // 1st   fonts1: array[0..4] of string = ('Verdana', 'Roboto', 'Arial', 'Helvetica', 'sans-serif');   texts1: array[0..5] of string = ('Psalm 91:1', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');   fontSize1: Integer = 22;   folderName1: string = '1_22.4px'; const // 2nd   fonts2: array[0..4] of string = ('Verdana', 'Roboto', 'Arial', 'Helvetica', 'sans-serif');   texts2: array[0..5] of string = ('Text analysis', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');   fontSize2: Integer = 16;   folderName2: string = '2_16px'; const // 3rd   fonts3: array[0..2] of string = ('Arial', 'Helvetica', 'Sans- serif');   texts3: array[0..8] of string = ('Strong''s', 'Hebrew', 'English', 'Morphology', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');   fontSize3: Integer = 12;   folderName3: string = '3_12px'; const // 4th   fonts4: array[0..2] of string = ('Arial', 'Helvetica', 'Sans- serif');   texts4: array[0..5] of string = ('Go to Parallel Hebrew', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');   fontSize4: Integer = 16;   folderName4: string = '4_16px';   // @TODO: Change the first number of folderName with the current Font-Family var   texts: array[0..0] of string;   fonts: array[0..0] of string; begin   ProccessArguments();   inherited Create(TheOwner);   StopOnException:=True;   // Specify simple text, one font(family), and one font-size:   if (s.Text<>'') then     begin       texts[0] := s.Text;       if s.FontFamily<>'' then         begin           fonts[0] := s.FontFamily;           GenerateImages(fonts, texts, s.FontSize, s.FontFamily);         end       else         begin           GenerateImages(fonts1, texts, s.FontSize, folderName1);         end;     end   else     begin       GenerateImages(fonts1, texts1, fontSize1, folderName1);       WriteLn('První dávka zkušebních obrázků (22.4px) byla uložena do složky "' + folderName1 + '".');       GenerateImages(fonts2, texts2, fontSize2, folderName2);       WriteLn('Druhá dávka zkušebních obrázků (16px) byla uložena do složky "' + folderName2 + '".');       GenerateImages(fonts3, texts3, fontSize3, folderName3);       WriteLn('Třetí dávka zkušebních obrázků (12px) byla uložena do složky "' + folderName3 + '".');       GenerateImages(fonts4, texts4, fontSize4, folderName4);       WriteLn('Čtvrtá dávka zkušebních obrázků (16px) byla uložena do složky "' + folderName4 + '".');     end; end; destructor TMyApplication.Destroy; begin   inherited Destroy; end; procedure TMyApplication.ProccessArguments();  var i: integer;  strA: Array of String;  cStr: String; begin   s.Drive              := '';   s.Path               := '';   s.Italic             := False;   s.Bold               := False;   s.BoldItalic         := False;   s.JustNormal         := False;   s.JustBoldItalic     := False;   s.Color              := clWhite;   s.BackgroundColor    := clBlack;   s.Groups             := [];   s.include_XOR        := False;   s.include_XNOR        := False;   s.separate_NBI       := False;   s.Text       := '';   s.FontSize         := 12;   s.FontFamily       := '';   s.Characters       := '';   for i := 1 to paramCount do     begin       // writeLn(i:2, '. argument: ', paramStr(i));       strA := paramStr(i).split('=');       cStr := strA[1];       Case strA[0] of        '-drive':          s.drive:=cStr;        '-path':          s.path:=cStr;        '-margin':          if cStr<>'' then            s.Margin:=StrtoInt(cStr)          else            s.Margin:=3;        '-text':          s.Text := cStr;        '-font-size':          s.FontSize := StrToInt(cStr);        '-font-family':          s.FontFamily := cStr; // @TODO: Strip quotes        '-color':          if (Length(cStr) > 0) and (cStr[1] = '#') then            s.Color := StringToColor(cStr)          else            begin              cStr:='cl'+AnsiUpperCase(cStr[1])+Copy(cStr, 2, Length(cStr) - 1);              s.Color := StringToColor(cStr);            end;        '-bgcolor':        if (Length(cStr) > 0) and (cStr[1] = '#') then            s.backgroundColor := StringToColor(cStr)          else          begin            cStr:='cl'+AnsiUpperCase(cStr[1])+Copy(cStr, 2, Length(cStr) - 1);            s.backgroundColor := StringToColor(cStr);          end;        '-XOR':          // This fixes non-sense arguments problem          if s.include_XNOR then            begin              s.include_XOR    := False;              s.include_XNOR   := False;              s.JustBoldItalic := True;              s.Groups := s.Groups-[TSNormal];              s.Groups := s.Groups+[TSBold];              s.Groups := s.Groups+[TSItalic];            end          else            s.Groups := [TSBold, TSItalic]; // Pouze skupina "bold" a skupina "italic"        '-XNOR':          // This fixes non-sense arguments problem          if s.include_XOR then            begin              s.include_XOR  := False;              s.include_XNOR := False;              s.JustNormal   := True;              s.Groups := s.Groups+[TSNormal];              s.Groups := s.Groups-[TSBold];              s.Groups := s.Groups-[TSItalic];            end          else            s.Groups := [TSNormal, TSBoldItalic]; // Pouze skupina "bold" a skupina "italic"        '-separate': s.separate_NBI:=True;        '-bold':          begin            s.Groups := s.Groups+[TSBold];            if s.Italic then              s.Groups := s.Groups+[TSBoldItalic];          end;        '-italic':          begin            s.Groups := s.Groups+[TSItalic];            if s.Bold then              s.Groups := s.Groups+[TSBoldItalic];          end;        '-b': s.Groups := s.Groups+[TSBold];        '-i': s.Groups := s.Groups+[TSItalic];        // The following option requires special code - if conditions        'BoldItalic': s.Groups := s.Groups-[TSNormal]+[TSBold]+[TSItalic];       end;     end;   if s.include_XOR then     begin       if (s.Groups = [TSBold]) OR (s.Groups = [TSItalic]) then         begin           s.Groups := s.Groups-[TSNormal];           s.Groups := s.Groups-[TSBoldItalic];         end;     end   else     begin     if s.separate_NBI then       begin         if (s.Groups = [TSBold]) OR (s.Groups = [TSItalic]) then           begin           s.Groups := s.Groups-[TSBoldItalic];           end;       end     else       begin       if (s.Groups = [TSBold]) OR (s.Groups = [TSItalic]) then           s.Groups := s.Groups-[TSNormal];       end;     end; end; procedure TMyApplication.WriteHelp; begin   { add your help code here }   writeln('Usage: ', ExeName, ' -h'); end; function TMyApplication.FontIsAvailable(const FontName: string): Boolean; begin   Result := Screen.Fonts.IndexOf(FontName) >= 0; end; procedure TMyApplication.GenerateImageWithStyle(p: TGenerateImageParams; fs: TFontStyles ); var filename: String;   R: TRect; begin   p.bmp.Canvas.Font.Style := fs;   R.TopLeft := Point(0, 0);   R.BottomRight := TPoint(p.bmp.Canvas.TextExtent(p.text));   p.bmp.SetSize(R.Right + 2*s.Margin, R.Bottom + 2*s.Margin);   writeln(p.folderName);   if not DirectoryExists(p.folderName) then     mkdir(p.folderName);   if s.path<>'' then      p.folderName := s.path + '/' + p.folderName   else if s.drive<>'' then       p.folderName := s.drive + '/' + p.folderName;   fileName := IncludeTrailingPathDelimiter(p.folderName) + p.font + ' ' + Copy(p.text, 1, Pos(' ', p.text) - 1);   // @TODO: MODIFY THIS BLOCK:   if s.Italic then     fileName := fileName + '_i'   else if s.Bold then     fileName := fileName + '_b';   fileName := fileName + '.bmp';   p.bmp.Canvas.Brush.Color := s.backgroundColor;   p.bmp.Canvas.FillRect(0, 0, p.bmp.Width, p.bmp.Height);   p.bmp.Canvas.TextOut(s.Margin, s.Margin, p.text);   p.bmp.SaveToFile(fileName); end; procedure TMyApplication.GenerateImages(fonts: array of string; texts: array of string; fontSize: Integer; folderNameShort: string);   var     i, j: Integer;     GroupStyle: TMyImgGrpPreferences;     p: TGenerateImageParams; begin   p.bmp := TBitmap.Create;   try     for i := 0 to High(fonts) do     begin       p.font:=fonts[i];       p.folderName := copy(folderNameShort,3,100);       if not FontIsAvailable(p.font) then         continue;       p.folderName := p.font+#32+p.folderName;       for j := 0 to High(texts) do       begin         p.text:=texts[j];         // BLOCK GENERATION         p.bmp.Canvas.Font.Name := p.font;         p.bmp.Canvas.Font.Size := fontSize;         p.bmp.Canvas.Font.Color := s.color;         // BLOCK OF CONTROL:         for GroupStyle in s.Groups do         begin           // Set the font style for the group to be processed           case GroupStyle of             TSNormal: GenerateImageWithStyle(p, []);             TSBold: GenerateImageWithStyle(p,  [fsBold]);             TSItalic: GenerateImageWithStyle(p, [fsItalic]);             TSBoldItalic: GenerateImageWithStyle(p, [fsBold, fsItalic]);           end;         end;       end;     end;   finally     p.bmp.Free;   end; end; var   Application: TMyApplication; begin   Application:=TMyApplication.Create(nil);   Application.Title:='Generate sample images with Text';   Application.Run;   Application.Free; end. PS: The type TGenerateImageParams probably should be renamed for something else because it contains the bmp object. TODO list: 1) @TODO: Change the first number of folderName with the current Font- Family folderName4: string = '4_16px'; 2) cStr; // done: Strip double quotes when first value assigned to cStr 3) // @TODO: MODIFY THIS BLOCK: There is need to change the names according the current style. ---------------------------------------------------------------------- TRon · 2023-11-11 23:59 Quote from: barracuda on November 11, 2023, 09:24:44 pmI've just finished the implementation https://texture-edit.blogspot.com/2023/11/groups-of-images-generator- chatgpt.html Another update, ok  QuoteI will implement three new parameters: -margin, -text , -characters I understand the margin and the text parameters. Regarding the characters parameter I would like to ask if that list of characters from the help is a static list of characters or that the user can provide a custom list of characters ? It is also not entirely clear for me so ask here as well: when this parameter is provided is the program suppose to write a bitmap for every individual character ? In case both questions are answered with yes then you can 'abuse' the text parameter to contain a set of characters and use a separate parameter switch to indicate that each character from the text parameter should be treated by the program as individual characters. Doing so will avoid ambiguity between the text and characters parameters and on top of that would also be easier to implement. QuoteI will read your answer tomorow. Thank you. Also thank you for your politeness. Take your time and as long as you are making progress with your program the way you want to you can ignore al the bit and pieces that are not relevant for you. There is no need to thank me as you are the one having done all the work yourself. I only try to point out some things here and there in the hope that it will be able to help you out. I still think you are over-complicating some things (but perhaps I am over-simplifying it  ) . We'll see. I await your answers/progress so that I can adjust my test-example and will post that so you are able to see how some things could be implemented differently. In the end it does not matter much because as long as the program does what you want it do do then you've reached your goal. There are many ways that lead to Rome ---------------------------------------------------------------------- barracuda · 2023-11-12 09:09 "Regarding the characters parameter I would like to ask if that list of characters from the help is a static list of characters or that the user can provide a custom list of characters ?" non-static, defined by user. "It is also not entirely clear for me so ask here as well: when this parameter is provided is the program suppose to write a bitmap for every individual character ?" For every individual character - Yes. The -text is used to skip the predefined arrays of text. So when you need just few images, you can use -text. To see if the app works, I don't need to create 300 images, I can check it with 20 images. Hence I wanted to add this to simplify the tests. The plans The -characters will be used to create final product. With -text I can see the whole text how it looks. With -characters I will collect some special data about the characters and I would like to save that data later. I will explain this later. There will be few edit functions, and the results will be added to a db file. I did not work with db yet, but I know SQL a bit. In the first stage of this project we generate images, in next stage we will save them (the individual characters) to more reasonable storage. In next stage will need to read them and view them or extract them. But for viewing it would be better to rewrite the code to viewer (but the viewer is very low priority; we already created a viewer in on topic, where I asked about it). The generated images of characters saved to db will be very important for me when I will develop next project (probably a dll, and then probably an app in ahk - because ahk seems to me a little bit easier to me to reach final goal; this image genaration is just a small tool to help a little bit to collect text-image data). The last paragraph "In case both questions are answered with yes then you can 'abuse' the text parameter to contain a set of characters and use a separate parameter switch to indicate that each character from the text parameter should be treated by the program as individual characters." It seems to me better to use different params because -text shows some information like a sentence or title. While -characters don't make words or a sentence. It's different function. The -text is save just to image files, but -characters would be better to save to file. ---------------------------------------------------------------------- TRon · 2023-11-12 11:27 @barracuda: This is my take based on your specifications. It is Free Pascal only, does not save the images (only emits what it could do with what settings), is lacking proper error handling and does not have all the (detailed) features that you mentioned supported (you are starting to divert/expand too much from your original goal so I have to stop implementing these new features as I simply got other projects to attend to). So all the FPImage drawing routines, use of Freetype fonts and all that stiff was removed from the source (hence the short suffix in the sourc-code-name  ). This 'half baked' implementation exist for two reasons: - to show that some things can be done/handled differently - I wanted to try and make this a FPC only utility but failed miserably when working with the FreeType library (and other relevant support units) because of lacking functionality with regards to font family names and applying styles. The help only provides information for those arguments that I handle differently. You can play with the command-line options and verify if it meets your requirements. To my knowledge it allows for all your expressed features to be performed, just using another solution to accomplish things. Some detailed features are missing though these are marked in the source-code as TODO. Have Fun ! ---------------------------------------------------------------------- barracuda · 2023-11-12 15:12 Thx. This is pretty clear code. How is this semantics called when you assign the data in declaration block? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i ProcessItems : array of TProcessItem =  (    ( What is meanining of "@" in is it a pointer - reference? I did not see this yet, so I doubt that this is a pointer mark. Code: [Select]ch := getLongOpts('hc', @options[1], optionIndex); I've realized I should separate the image genaration and the save action. Code: [Select]// Program to generate combinations of images // With text of various fonts and styles // The text images completly depends on user settings // Usual use of this program is to genarate at least two group of images, which one uses Normal style. program project1; {$mode objfpc}{$H+} uses   {$IFDEF UNIX}   cthreads,   {$ENDIF}   Classes, SysUtils, CustApp,   Graphics,   LCLType, LCLIntf,   Forms, Interfaces; // required for Screen unit // enum and record for "settings" or configuration of the app // i.e. TMyImgGrpPreferences & TSettings type TMyImgGrpPreferences = (TSNormal, TSBold, TSItalic, TSBoldItalic); type   TSettings = record     Drive: string;     Path: string;     Color: TColor;     BackgroundColor: TColor;     include_XOR: Boolean; // disables TSNormal, TSBoldItalic but must be before bold/italic specifiers     include_XNOR: Boolean; // disables TSBold, TSItalic, enables TSNormal, TSBoldItalic     separate_NBI: Boolean; // Don't mix Bold+Italic     Italic: Boolean; // Italic or Bold can be generated together     Bold: Boolean;   // Italic or Bold can be generated together     BoldItalic: Boolean; // Generate only the BoldItalic instead Bold or Italic or Normal     JustNormal: Boolean; // This is just for unusual parameters like program.exe -XOR -XNOR     JustBoldItalic: Boolean; // This is just for unusual parameters like program.exe -XNOR -XOR     Groups: Set of TMyImgGrpPreferences;     Margin: Byte; // -margin Margin around text     Text: String; // -text When you want to test just one text string, not an array     FontFamily: String; // For use with -text     FontSize: Byte; // For use with -text     Characters: String; // -characters When you want to create images of characters as defined here   end; type  TGenerateImageParams = record     GroupStyles: TMyImgGrpPreferences;     bmp: TBitmap;     folderName, font, text: string;     i, j: Integer;     R: TRect;   end;   { TMyApplication }   TMyApplication = class(TCustomApplication)   protected     procedure DoRun; override;   private     { Private declarations }     procedure ProccessArguments();     procedure GenerateImages(fonts: array of string; texts: array of string; fontSize: Integer; folderNameShort: string);     procedure GenerateImageWithStyle(p: TGenerateImageParams; fs: TFontStyles);     procedure SaveImageToFile(p: TGenerateImageParams);     function  FontIsAvailable(const FontName: string): Boolean;   public     constructor Create(TheOwner: TComponent); override;     destructor Destroy; override;     procedure WriteHelp; virtual;   end; { TMyApplication } var s: TSettings; procedure TMyApplication.DoRun;   var ErrorMsg: String; begin   // quick check parameters   ErrorMsg:=CheckOptions('h', 'help');   if ErrorMsg<>'' then begin     ShowException(Exception.Create(ErrorMsg));     Terminate;     Exit;   end;   // parse parameters   if HasOption('h', 'help') then begin     WriteHelp;     Terminate;     Exit;   end;   { add your program here }   // stop program loop   Terminate; end; constructor TMyApplication.Create(TheOwner: TComponent); const // 1st   fonts1: array[0..4] of string = ('Verdana', 'Roboto', 'Arial', 'Helvetica', 'sans-serif');   texts1: array[0..5] of string = ('Psalm 91:1', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');   fontSize1: Integer = 22;   folderName1: string = '1_22.4px'; const // 2nd   fonts2: array[0..4] of string = ('Verdana', 'Roboto', 'Arial', 'Helvetica', 'sans-serif');   texts2: array[0..5] of string = ('Text analysis', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');   fontSize2: Integer = 16;   folderName2: string = '2_16px'; const // 3rd   fonts3: array[0..2] of string = ('Arial', 'Helvetica', 'Sans- serif');   texts3: array[0..8] of string = ('Strong''s', 'Hebrew', 'English', 'Morphology', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');   fontSize3: Integer = 12;   folderName3: string = '3_12px'; const // 4th   fonts4: array[0..2] of string = ('Arial', 'Helvetica', 'Sans- serif');   texts4: array[0..5] of string = ('Go to Parallel Hebrew', 'Slovo', 'AHK pro', 'Vygenerování zkušebních obrázků.', 'Vygenerování zkušebních obrázků.', 'Včera ráno, dnes odpoledne a v neděli večer');   fontSize4: Integer = 16;   folderName4: string = '4_16px';   // @TODO: Change the first number of folderName with the current Font-Family var   texts: array[0..0] of string;   fonts: array[0..0] of string; begin   ProccessArguments();   inherited Create(TheOwner);   StopOnException:=True;   // Specify simple text, one font(family), and one font-size:   if (s.Text<>'') then     begin       texts[0] := s.Text;       if s.FontFamily<>'' then         begin           fonts[0] := s.FontFamily;           GenerateImages(fonts, texts, s.FontSize, s.FontFamily);         end       else         begin           GenerateImages(fonts1, texts, s.FontSize, folderName1);         end;     end   else     begin       GenerateImages(fonts1, texts1, fontSize1, folderName1);       WriteLn('První dávka zkušebních obrázků (22.4px) byla uložena do složky "' + folderName1 + '".');       GenerateImages(fonts2, texts2, fontSize2, folderName2);       WriteLn('Druhá dávka zkušebních obrázků (16px) byla uložena do složky "' + folderName2 + '".');       GenerateImages(fonts3, texts3, fontSize3, folderName3);       WriteLn('Třetí dávka zkušebních obrázků (12px) byla uložena do složky "' + folderName3 + '".');       GenerateImages(fonts4, texts4, fontSize4, folderName4);       WriteLn('Čtvrtá dávka zkušebních obrázků (16px) byla uložena do složky "' + folderName4 + '".');     end; end; destructor TMyApplication.Destroy; begin   inherited Destroy; end; procedure TMyApplication.ProccessArguments();  var i: integer;  strA: Array of String;  cStr: String; begin   s.Drive              := '';   s.Path               := '';   s.Italic             := False;   s.Bold               := False;   s.BoldItalic         := False;   s.JustNormal         := False;   s.JustBoldItalic     := False;   s.Color              := clWhite;   s.BackgroundColor    := clBlack;   s.Groups             := [];   s.include_XOR        := False;   s.include_XNOR        := False;   s.separate_NBI       := False;   s.Text       := '';   s.FontSize         := 12;   s.FontFamily       := '';   s.Characters       := '';   for i := 1 to paramCount do     begin       // writeLn(i:2, '. argument: ', paramStr(i));       strA := paramStr(i).split('=');       cStr := AnsiDequotedStr(strA[1],'"');       Case strA[0].ToLower of        '-drive':          s.drive:=cStr;        '-path':          s.path:=cStr;        '-margin':          if cStr<>'' then            s.Margin:=StrtoInt(cStr)          else            s.Margin:=3;        '-text':          s.Text := cStr;        '-font-size':          s.FontSize := StrToInt(cStr);        '-font-family':          s.FontFamily := cStr;        '-color':          if (Length(cStr) > 0) and (cStr[1] = '#') then            s.Color := StringToColor(cStr)          else            begin              cStr:='cl'+AnsiUpperCase(cStr[1])+Copy(cStr, 2, Length(cStr) - 1);              s.Color := StringToColor(cStr);            end;        '-bgcolor':        if (Length(cStr) > 0) and (cStr[1] = '#') then            s.backgroundColor := StringToColor(cStr)          else          begin            cStr:='cl'+AnsiUpperCase(cStr[1])+Copy(cStr, 2, Length(cStr) - 1);            s.backgroundColor := StringToColor(cStr);          end;        '-xor':          // This fixes non-sense arguments problem          if s.include_XNOR then            begin              s.include_XOR    := False;              s.include_XNOR   := False;              s.JustBoldItalic := True;              s.Groups := s.Groups-[TSNormal];              s.Groups := s.Groups+[TSBold];              s.Groups := s.Groups+[TSItalic];            end          else            s.Groups := [TSBold, TSItalic]; // Pouze skupina "bold" a skupina "italic"        '-xnor':          // This fixes non-sense arguments problem          if s.include_XOR then            begin              s.include_XOR  := False;              s.include_XNOR := False;              s.JustNormal   := True;              s.Groups := s.Groups+[TSNormal];              s.Groups := s.Groups-[TSBold];              s.Groups := s.Groups-[TSItalic];            end          else            s.Groups := [TSNormal, TSBoldItalic]; // Pouze skupina "bold" a skupina "italic"        '-separate': s.separate_NBI:=True;        '-bold':          begin            s.Groups := s.Groups+[TSBold];            if s.Italic then              s.Groups := s.Groups+[TSBoldItalic];          end;        '-italic':          begin            s.Groups := s.Groups+[TSItalic];            if s.Bold then              s.Groups := s.Groups+[TSBoldItalic];          end;        '-b': s.Groups := s.Groups+[TSBold];        '-i': s.Groups := s.Groups+[TSItalic];        // The following option requires special code - if conditions        'bolditalic': s.Groups := s.Groups-[TSNormal]+[TSBold]+[TSItalic];       end;     end;   if s.include_XOR then     begin       if (s.Groups = [TSBold]) OR (s.Groups = [TSItalic]) then         begin           s.Groups := s.Groups-[TSNormal];           s.Groups := s.Groups-[TSBoldItalic];         end;     end   else     begin     if s.separate_NBI then       begin         if (s.Groups = [TSBold]) OR (s.Groups = [TSItalic]) then           begin           s.Groups := s.Groups-[TSBoldItalic];           end;       end     else       begin       if (s.Groups = [TSBold]) OR (s.Groups = [TSItalic]) then           s.Groups := s.Groups-[TSNormal];       end;     end; end; procedure TMyApplication.WriteHelp; begin   { add your help code here }   writeln('Usage: ', ExeName, ' -h'); end; function TMyApplication.FontIsAvailable(const FontName: string): Boolean; begin   Result := Screen.Fonts.IndexOf(FontName) >= 0; end; procedure TMyApplication.GenerateImageWithStyle(p: TGenerateImageParams; fs: TFontStyles ); var R: TRect; begin   p.bmp.Canvas.Font.Style := fs;   R.TopLeft := Point(0, 0);   R.BottomRight := TPoint(p.bmp.Canvas.TextExtent(p.text));   p.bmp.SetSize(R.Right + 2*s.Margin, R.Bottom + 2*s.Margin);   p.bmp.Canvas.Brush.Color := s.backgroundColor;   p.bmp.Canvas.FillRect(0, 0, p.bmp.Width, p.bmp.Height);   p.bmp.Canvas.TextOut(s.Margin, s.Margin, p.text); end; procedure TMyApplication.SaveImageToFile(p: TGenerateImageParams); var filename: String; begin   writeln(p.folderName);   if not DirectoryExists(p.folderName) then     mkdir(p.folderName);   if s.path<>'' then      p.folderName := s.path + '/' + p.folderName   else if s.drive<>'' then       p.folderName := s.drive + '/' + p.folderName;   fileName := IncludeTrailingPathDelimiter(p.folderName) + p.font + ' ' + Copy(p.text, 1, Pos(' ', p.text) - 1);   // @TODO: MODIFY THIS BLOCK:   if s.Italic then     fileName := fileName + '_i'   else if s.Bold then     fileName := fileName + '_b';   fileName := fileName + '.bmp';   p.bmp.SaveToFile(fileName); end; procedure TMyApplication.GenerateImages(fonts: array of string; texts: array of string; fontSize: Integer; folderNameShort: string);   var     i, j: Integer;     GroupStyle: TMyImgGrpPreferences;     p: TGenerateImageParams; begin   p.bmp := TBitmap.Create;   try     for i := 0 to High(fonts) do     begin       p.font:=fonts[i];       p.folderName := copy(folderNameShort,3,100);       if not FontIsAvailable(p.font) then         continue;       p.folderName := p.font+#32+p.folderName;       for j := 0 to High(texts) do       begin         p.text:=texts[j];         // BLOCK GENERATION         p.bmp.Canvas.Font.Name := p.font;         p.bmp.Canvas.Font.Size := fontSize;         p.bmp.Canvas.Font.Color := s.color;         // BLOCK OF CONTROL:         for GroupStyle in s.Groups do         begin           // Set the font style for the group to be processed           case GroupStyle of             TSNormal:               begin                 GenerateImageWithStyle(p, []);                 SaveImageToFile(p);               end;             TSBold:               begin                 GenerateImageWithStyle(p,  [fsBold]);                 SaveImageToFile(p);               end;             TSItalic:               begin                 GenerateImageWithStyle(p, [fsItalic]);                 SaveImageToFile(p);               end;             TSBoldItalic:               begin                 GenerateImageWithStyle(p, [fsBold, fsItalic]);                 SaveImageToFile(p);               end;           end;         end;       end;     end;   finally     p.bmp.Free;   end; end; var   Application: TMyApplication; begin   Application:=TMyApplication.Create(nil);   Application.Title:='Generate sample images with Text';   Application.Run;   Application.Free; end. Last modification is to the filename ending: Code: [Select]procedure TMyApplication.GenerateImageWithStyle(p: TGenerateImageParams; fs: TFontStyles ); var R: TRect; begin   p.bmp.Canvas.Font.Style := fs;   R.TopLeft := Point(0, 0);   R.BottomRight := TPoint(p.bmp.Canvas.TextExtent(p.text));   p.bmp.SetSize(R.Right + 2*s.Margin, R.Bottom + 2*s.Margin);   p.bmp.Canvas.Brush.Color := s.backgroundColor;   p.bmp.Canvas.FillRect(0, 0, p.bmp.Width, p.bmp.Height);   p.bmp.Canvas.TextOut(s.Margin, s.Margin, p.text); end; procedure TMyApplication.SaveImageToFile(p: TGenerateImageParams; FileNameEnding: String); var filename: String; begin   writeln(p.folderName);   if not DirectoryExists(p.folderName) then     mkdir(p.folderName);   if s.path<>'' then      p.folderName := s.path + '/' + p.folderName   else if s.drive<>'' then       p.folderName := s.drive + '/' + p.folderName;   fileName := IncludeTrailingPathDelimiter(p.folderName) + p.font + ' ' + Copy(p.text, 1, Pos(' ', p.text) - 1);   // Ending specifies the style and possibly the character   fileName := fileName + fileNameEnding +'.bmp';   p.bmp.SaveToFile(fileName); end; ---------------------------------------------------------------------- TRon · 2023-11-12 16:44 Quote from: barracuda on November 12, 2023, 04:12:55 pmThx. This is pretty clear code. Thank you and please feel free to copy-paste from it in case it helps your project. There are some other small items in there that could benefit your code when you would use them. I'll let you be the judge of that. QuoteHow is this semantics called when you assign the data in declaration block? The ProcessItems variable is declared as a dynamic array. More information in the wiki and the manual Dynamic arrays can (since Free Pascal 3.2) be declared as a constant expression see manual. QuoteWhat is meanining of "@" in is it a pointer - reference? I did not see this yet, so I doubt that this is a pointer mark. It is indeed a pointer. It is a special operator that takes the address of a variable, procedure or function, see manual It is shorthand for the Addr function. The reason to use it is because the GetLongOpts function from unit getopts requires to pass the first entry of a option array (parameter LongOpts). The reason that I do not have my longopt array declared as a dynamic array (which is an option) is because the unit originates from before Free Pascal version 3.2 so the implementation is bound to an array index that starts with the number one (instead of zero which would normally be used for arrays). If you do not use an array starting with index one then the variable OptInd will be out of sync with the array, which works a bit confusing and counterproductive imho. QuoteI've realized I should separate the image genaration and the save action. I already see room for improvement(s) and clarity there, but I'll not bother you with it right now. Code can always be made cleaner later on. If you have other questions then feel free to ask ---------------------------------------------------------------------- barracuda · 2023-11-12 23:34 Hence the @ is the same as ^ or * but reffering to heap. It's quite long time ago when I tried to learn C/C++ but no success. Pascal is easier. I see I will need to use dynamic arrays (not the static ones) to make the characters pretty working for all fonts, fonts and font sizes. I will need to redesign. Using options like Code: [Select]project.exe -bold -italic -characters="..." -font- family="Arial, Verdana" -font-size="9, 10, 12" -font-family="Times New Roman,Courier New" -font-size="14, 12, 10" to add more fonts to process It could be possible to read the options from file, where every option would be place on a single line of file like that: Code: [Select]bold italic characters="..." font-family="Arial, Verdana" font-size="9, 10, 12" font-family="Times New Roman,Courier New" font-size="14, 12, 10" It seems to be more clear. Then I could run it like that: Code: [Select]program.exe config_file="project1.cfg" ---------------------------------------------------------------------- TRon · 2023-11-13 00:07 Quote from: barracuda on November 13, 2023, 12:34:10 amHence the @ is the same as ^ or * but reffering to heap. It's quite long when I tried to learn C/C++ but no success. Pascal is easier. It indeed reads a lot easier in Pascal than it does for C/C++ (However, I am biased  ) On the other hand, if you are old-school, then you are probably not that familiar with new features such as the dynamic arrays, generics, etc. But they are quite easy to understand/grasp (at least when you start with a slow introduction). QuoteI will need to redesign. Using options like Code: [Select]project.exe -bold -italic -characters "..." -font-family "Arial,Verdana" -font-size "9, 10, 12" -font-family "Times New Roman,Courier New" -font-size "14, 12, 10" to add more fonts to process Don't overthink/overcomplicate by wanting to do everything on a single command-run. The above is the same as: project.exe -bold -italic -characters "..." -font-family "Arial,Verdana" -font-size "9, 10, 12" project.exe -bold -italic -characters "..." -font-family "Times New Roman,Courier New" -font-size "14, 12, 10" And yes there is some duplication there, but you can automate that in one of two ways: - abstract the commandline parameter handling away from paramstr/paramcount. That way you can call the the parameter handling routine multiple times but with different options. - use a script (or a small invoke utility) to call your program and which supplies the parameters. The duplicated parameters can be stored in a (shell) variable for re-use. Actually, when I started implementing I had your arrays stored inside an inifile for easy editing/adding new sets of arrays (fonts, texts, fontsize, foldername) and loading these arrays from that file. I removed that functionality as you did not seem to focus on extending those array (perhaps later you will, I don't know  ) . You could do something similar for the individual character drawing so that for longer sets of characters and family names you can simply add them to such an ini-file. Dunno, if something like that could help you out. I am missing the big picture here. I simply can't imagine that you would want to do something like you showed in your example, let's say, a hundred times for 80 different font families applying all 4 different font-styles and doing so for 800 different (unicode) characters. ---------------------------------------------------------------------- barracuda · 2023-11-13 09:38 Yes the long command line is not good solution so I would prefer to create a simple cfg file because that is the best approach. I can have like 10 cfgs with examples for everyone. Simple and easy. So I would probably use TStringList and TStringList.loadfromFile ... as this is what I used in Delphi 7, but I see you're using new approaches. ---------------------------------------------------------------------- barracuda · 2023-11-13 11:41 I think I am almost done with new parsing function which prepares the arguments read either from command line or from the file. I changed a little bit the specification (not reflected in manual) so the "-" before a parametr is not a need now, but it should if present, it will crop it. I have question in the section of last block, where my questio is on easy key access solution. Code: [Select]for i := 0 to Pars.Count-1 do   begin     if Pars.Values[i][1]='-' then       Pars.Values[i]:=copy(Pars.Values[i],2,length(Pars.Values[i]));     Pars.Values[i] := ToLower+'='+AnsiDequotedStr(Pars.Values[i],'"'));   end; Code: [Select]Pars.Values[i] ... to change to Code: [Select]Pars.Values[Pars.Keys[i]] does not seem to me good, because the semantics would be too long. Code: [Select]procedure TMyApplication.prepareParms(var Pars: TStringList); var i: integer;   sFN: string;   strA: Array of String; begin   Pars.NameValueSeparator := '=';   sFN := '';   for i := 1 to paramCount do       begin         strA := paramStr(i).split('=');         if strA[0][1]='-' then           strA[0]:=copy(strA[0],2,length(strA[0]));         Pars.Add(strA[0].ToLower+'='+AnsiDequotedStr(strA[1],'"'));       end;   if Pars.count>0 then     begin       for i := 0 to Pars.count-1 do           begin             if (Pars.Names[i]='configfile') OR (Pars.Names[i]='config- file') OR (Pars.Names[i]='config') then               begin                 sFN := AnsiDequotedStr(Pars.Values[Pars.Names[i]],'"');                 break;               end;           end;       if not fileexists(sFN) then          writeln('File not found '+sFN)       else         begin           writeln('Reading config file '+sFN);           Pars.Free;           Pars.LoadFromFile(sFN);           for i := 0 to Pars.Count-1 do               begin                 if Pars.Values[i][1]='-' then                   Pars.Values[i]:=copy(Pars.Values[i],2,length(Pars.Values[i]));                 Pars.Values[i] := AnsiDequotedStr(Pars.Values[i],'"'));               end;         end;     end; end; ---------------------------------------------------------------------- TRon · 2023-11-13 23:07 Quote from: barracuda on November 13, 2023, 12:41:18 pmI have question in the section of last block, where my questio is on easy key access solution. I am not entirely sure how exactly you want to accomplish things because you did not share how you want/have the parameters stored in the config file. However, I believe that when you wrote: Code: [Select]for i := 0 to Pars.Count-1 do   begin     if Pars.Values[i][1]='-' then       Pars.Values[i]:=copy(Pars.Values[i],2,length(Pars.Values[i]));     Pars.Values[i] := ToLower+'='+AnsiDequotedStr(Pars.Values[i],'"'));   end; .. is that you probably meant to write the following instead: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i       for i := 0 to Pars.Count-1 do      begin        if Pars.Strings[i][1]='-'        then Pars.Strings[i]:=copy(Pars.Strings[i],2,length(Pars.Strings[i]));      Pars.Strings[i] := AnsiDequotedStr(Pars.Strings[i],'"');      end; .. but as said, I am not 100% sure. What I am more sure about it that just above that when you wrote: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   Parse.Free;  ... is that you actually meant to write: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   Parse.Clear;  ... If not mistaken. In case mistaken then you (still) have yourself an illegal access error ---------------------------------------------------------------------- barracuda · 2023-11-13 23:49 Hey I was just going to pass this and you passed the code. So this is correction suggested by chatGPT using Pars.ValueFromIndex. I originally followed this guide https://delphiprogrammingdiary.blogspot.com/2017/09/tstringlist-name- value-pair.html but it seems not to work, so I used ValueFromIndex: Code: [Select]procedure TMyApplication.prepareParms(var Pars: TStringList); var i: integer;   sFN: string;   strA: Array of String; begin   Pars.NameValueSeparator := '=';   sFN := '';   for i := 1 to paramCount do       begin         strA := paramStr(i).split('=');         if strA[0][1]='-' then           strA[0]:=copy(strA[0],2,length(strA[0]));         Pars.Add(strA[0].ToLower+'='+AnsiDequotedStr(strA[1],'"'));       end;   if Pars.count>0 then     begin       for i := 0 to Pars.count-1 do           begin             if (Pars.Names[i]='configfile') OR (Pars.Names[i]='config- file') OR (Pars.Names[i]='config') then               begin                 sFN := AnsiDequotedStr(Pars.ValueFromIndex[i],'"');                 break;               end;           end;       if not fileexists(sFN) then          writeln('File not found '+sFN)       else         begin           writeln('Reading config file '+sFN);           Pars.Free;           Pars.LoadFromFile(sFN);           for i := 0 to Pars.Count-1 do               begin                 if Pars.ValueFromIndex[i][1]='-' then                   Pars.ValueFromIndex[i]:=copy(Pars.ValueFromIndex[i], 2,length(Pars.ValueFromIndex[i]));                 Pars.ValueFromIndex[i] := AnsiDequotedStr(Pars.Values[Pars.ValueFromIndex[i]],'"');               end;         end;     end; end; The config: Code: [Select]bold italic characters="..." font-family="Arial, Verdana" font-size="9, 10, 12" font-family="Times New Roman,Courier New" font-size="14, 12, 10" I will continue tommorow (I did not debug it yet). Edit: Yes I mean .clear not .free . ---------------------------------------------------------------------- barracuda · 2023-11-14 12:24 pars stands for parameters. It's not clear why I cannot watch the values in Debug/Add Watch or Debug/Evaluate in the private procedure. This should be not problem in Delphi 7. How does FreePascal manage. I can display the values by writeln but that is not good approach for debugging - needs to compile every time when I add the line Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i writeln(Pars.ValueFromIndex[i]); In Debug window like Debug Inspector, etc. I always see "inavailable" Error: Member not found. Which is odd, because the member is there, but it seems like no access to private scope... I can read variables like strA, strA[0] and sFN. But the var Pars: TStringList is passed like that: Code: [Select]  Pars := TStringList.create;   Pars.NameValueSeparator := '=';   prepareParms(pars); So to debug I need to see Pars.count and other Pars members like .Names and .Values ---------------------------------------------------------------------- TRon · 2023-11-14 22:11 Quote from: barracuda on November 14, 2023, 01:24:15 pmIt's not clear why I cannot watch the values in Debug/Add Watch or Debug/Evaluate in the private procedure. This should be not problem in Delphi 7. How does FreePascal manage. I can display the values by writeln but that is not good approach for debugging - needs to compile every time when I add the line Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i writeln(Pars.ValueFromIndex[i]); In Debug window like Debug Inspector, etc. I always see "inavailable" Error: Member not found. Which is odd, because the member is there, but it seems like no access to private scope... 2 relevant threads (there are probably plenty more but these two seem to sum it up nicely): - Inspect into TStringList - Watching TStringList - Lazarus 3.0 (FpDebug) And ofc. you need to have the RTL compiled with debug. It is a work in progress.... BTW: For all these string manipulation that you seem to be doing, have a look at unit StrUtils or the typehelper TStringHelper. It reads a lot nicer (and understandable) than those calls to copy(). ---------------------------------------------------------------------- barracuda · 2023-11-14 22:55 I followed the first link and this command Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i PStringItemList(Pars.FList)^[0]really does the job. The link is quite helpful. Edit: Now I see there is  Substring() Code: [Select]public function TStringHelper.Substring(   AStartIndex: SizeInt ):string; overload; function TStringHelper.Substring(   AStartIndex: SizeInt;   ALen: SizeInt ):string; overload;