Jelajahi Sumber

Update repository

Curtis Hamilton 4 minggu lalu
induk
melakukan
b3e2705226

+ 1 - 1
Makefile

@@ -2,7 +2,7 @@
 # Don't edit, this file is generated by FPCMake Version 2.0.0
 #
 default: help
-MAKEFILETARGETS=i386-linux i386-go32v2 i386-win32 i386-os2 i386-freebsd i386-beos i386-haiku i386-netbsd i386-solaris i386-netware i386-openbsd i386-wdosx i386-darwin i386-emx i386-watcom i386-netwlibc i386-wince i386-embedded i386-symbian i386-nativent i386-iphonesim i386-android i386-aros m68k-linux m68k-netbsd m68k-amiga m68k-atari m68k-palmos m68k-macosclassic m68k-embedded m68k-sinclairql m68k-human68k powerpc-linux powerpc-netbsd powerpc-amiga powerpc-macosclassic powerpc-darwin powerpc-morphos powerpc-embedded powerpc-wii powerpc-aix sparc-linux sparc-netbsd sparc-solaris sparc-embedded x86_64-linux x86_64-freebsd x86_64-haiku x86_64-netbsd x86_64-solaris x86_64-openbsd x86_64-darwin x86_64-win64 x86_64-embedded x86_64-iphonesim x86_64-android x86_64-aros x86_64-dragonfly arm-linux arm-netbsd arm-palmos arm-wince arm-gba arm-nds arm-embedded arm-symbian arm-android arm-aros arm-freertos arm-ios powerpc64-linux powerpc64-freebsd powerpc64-darwin powerpc64-embedded powerpc64-aix avr-embedded armeb-linux armeb-embedded mips-linux mipsel-linux mipsel-embedded mipsel-android mipsel-ps1 mips64-linux mips64el-linux jvm-java jvm-android i8086-embedded i8086-msdos i8086-win16 aarch64-linux aarch64-freebsd aarch64-darwin aarch64-win64 aarch64-embedded aarch64-iphonesim aarch64-android aarch64-ios wasm32-embedded wasm32-wasip1 wasm32-wasip1threads wasm32-wasip2 sparc64-linux riscv32-linux riscv32-embedded riscv32-freertos riscv64-linux riscv64-embedded xtensa-linux xtensa-embedded xtensa-freertos z80-embedded z80-zxspectrum z80-msxdos z80-amstradcpc loongarch64-linux
+MAKEFILETARGETS=i386-linux i386-go32v2 i386-win32 i386-os2 i386-freebsd i386-beos i386-haiku i386-netbsd i386-solaris i386-netware i386-openbsd i386-wdosx i386-darwin i386-emx i386-watcom i386-netwlibc i386-wince i386-embedded i386-symbian i386-nativent i386-iphonesim i386-android i386-aros m68k-linux m68k-netbsd m68k-amiga m68k-atari m68k-palmos m68k-macosclassic m68k-embedded m68k-sinclairql m68k-human68k powerpc-linux powerpc-netbsd powerpc-amiga powerpc-macosclassic powerpc-darwin powerpc-morphos powerpc-embedded powerpc-wii powerpc-aix sparc-linux sparc-netbsd sparc-solaris sparc-embedded x86_64-linux x86_64-freebsd x86_64-haiku x86_64-netbsd x86_64-solaris x86_64-openbsd x86_64-darwin x86_64-win64 x86_64-embedded x86_64-iphonesim x86_64-android x86_64-aros x86_64-dragonfly arm-linux arm-netbsd arm-palmos arm-wince arm-gba arm-nds arm-embedded arm-symbian arm-android arm-aros arm-freertos arm-ios powerpc64-linux powerpc64-darwin powerpc64-embedded powerpc64-aix powerpc64-freebsd avr-embedded armeb-linux armeb-embedded mips-linux mipsel-linux mipsel-embedded mipsel-android mipsel-ps1 mips64-linux mips64el-linux jvm-java jvm-android i8086-embedded i8086-msdos i8086-win16 aarch64-linux aarch64-freebsd aarch64-darwin aarch64-win64 aarch64-embedded aarch64-iphonesim aarch64-android aarch64-ios wasm32-embedded wasm32-wasip1 wasm32-wasip1threads wasm32-wasip2 sparc64-linux riscv32-linux riscv32-embedded riscv32-freertos riscv64-linux riscv64-embedded xtensa-linux xtensa-embedded xtensa-freertos z80-embedded z80-zxspectrum z80-msxdos z80-amstradcpc loongarch64-linux
 BSDs = freebsd netbsd openbsd darwin dragonfly
 UNIXs = linux $(BSDs) solaris qnx haiku aix
 LIMIT83fs = go32v2 os2 emx watcom msdos win16 atari human68k

+ 4 - 3
packages/fcl-md/demo/README.md

@@ -6,8 +6,9 @@ This directory contains 3 markdown parser examples:
 They are:
 
 * demomd - simple markdown parser and html renderer.
-* md2html - slightly more complete markdown-to-html converter/
-* md2fpdoc - simple version of a markdown - to fpdoc converter.
+* md2html - slightly more complete markdown-to-html converter.
+* md2fpdoc - simple version of a markdown to fpdoc converter.
+* md2latex - simple version of a markdown to LaTeX converter.
 
 ## conversion to fpdoc 
 
@@ -24,5 +25,5 @@ The headers determine what is generated for a given section:
 
 links must be rendered as \[text\]\(text\) or \[\]\(text\)
 
-You can find a simple example in the [sample.md](sample.md) file.
+You can find a simple example in the [sampledoc.md](sampledoc.md) file.
 

+ 6 - 36
packages/fcl-md/demo/demomd.lpi

@@ -13,6 +13,11 @@
       <UseAppBundle Value="False"/>
       <ResourceType Value="res"/>
     </General>
+    <CustomData Count="3">
+      <Item0 Name="OpenAPIBase"/>
+      <Item1 Name="OpenAPIConfig"/>
+      <Item2 Name="OpenAPIFile"/>
+    </CustomData>
     <BuildModes>
       <Item Name="Default" Default="True"/>
     </BuildModes>
@@ -28,42 +33,6 @@
         <Filename Value="demomd.lpr"/>
         <IsPartOfProject Value="True"/>
       </Unit>
-      <Unit>
-        <Filename Value="markdown.elements.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit>
-      <Unit>
-        <Filename Value="markdown.htmlrender.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit>
-      <Unit>
-        <Filename Value="markdown.scanner.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit>
-      <Unit>
-        <Filename Value="markdown.utils.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit>
-      <Unit>
-        <Filename Value="markdown.parser.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit>
-      <Unit>
-        <Filename Value="markdown.inlinetext.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit>
-      <Unit>
-        <Filename Value="markdown.render.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit>
-      <Unit>
-        <Filename Value="markdown.line.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit>
-      <Unit>
-        <Filename Value="markdown.delimiter.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit>
     </Units>
   </ProjectOptions>
   <CompilerOptions>
@@ -73,6 +42,7 @@
     </Target>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
+      <OtherUnitFiles Value="../src"/>
       <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
     <Linking>

+ 3 - 1
packages/fcl-md/demo/demomd.lpr

@@ -8,7 +8,9 @@ uses
   markdown.parser,
   markdown.inlinetext,
   markdown.htmlrender,
-  markdown.render, markdown.line, markdown.delimiter;
+  markdown.processors,
+  markdown.render
+  ;
 
 var
   Source,Dest : TStringList;

+ 103 - 0
packages/fcl-md/demo/md2latex.lpi

@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="12"/>
+    <General>
+      <Flags>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <Title Value="md2latex"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <CustomData Count="3">
+      <Item0 Name="OpenAPIBase"/>
+      <Item1 Name="OpenAPIConfig"/>
+      <Item2 Name="OpenAPIFile"/>
+    </CustomData>
+    <BuildModes>
+      <Item Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+    </RunParams>
+    <Units>
+      <Unit>
+        <Filename Value="md2latex.lpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="markdown.elements.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="markdown.htmlrender.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="markdown.scanner.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="markdown.utils.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="markdown.parser.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="markdown.inlinetext.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="markdown.render.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="markdown.line.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="markdown.delimiter.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target>
+      <Filename Value="md2latex"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <OtherUnitFiles Value="../src"/>
+      <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+    <Linking>
+      <Debugging>
+        <DebugInfoType Value="dsDwarf3"/>
+      </Debugging>
+    </Linking>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions>
+      <Item>
+        <Name Value="EAbort"/>
+      </Item>
+      <Item>
+        <Name Value="ECodetoolError"/>
+      </Item>
+      <Item>
+        <Name Value="EFOpenError"/>
+      </Item>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 49 - 0
packages/fcl-md/demo/md2latex.lpr

@@ -0,0 +1,49 @@
+program md2latex;
+
+{$mode objfpc}
+{$h+}
+
+uses
+  classes,
+  markdown.utils,
+  markdown.elements,
+  markdown.scanner,
+  markdown.parser,
+  markdown.inlinetext,
+  markdown.latexrender,
+  markdown.processors,
+  markdown.render
+  ;
+
+var
+  Source,Dest : TStringList;
+  Doc : TMarkDownDocument;
+  Renderer: TMarkDownLatexRenderer;
+
+begin
+  Dest:=Nil;
+  Renderer:=Nil;
+  Source:=TStringList.Create;
+  try
+    Dest:=TStringList.Create;
+    Source.LoadFromFile(ParamStr(1));
+    Doc:=TMarkDownParser.FastParse(Source,[]);
+    Renderer:=TMarkDownLatexRenderer.Create(Nil);
+    With Renderer do
+      begin 
+      Options:=[loEnvelope];
+      If ParamStr(2)='' then
+        Writeln(RenderLaTeX(Doc))
+      else
+        begin
+        RenderDocument(Doc,Dest);
+        Dest.SaveToFile(ParamStr(2));
+        end;
+      end;
+  finally
+    Renderer.Free;
+    Source.Free;
+    Dest.Free;
+  end;
+end.
+

+ 8 - 0
packages/fcl-md/fpmake.pp

@@ -90,6 +90,14 @@ begin
       AddUnit('markdown.render');
       end;
 
+    T:=P.Targets.AddUnit('markdown.latexrender.pas');
+    with T.Dependencies do
+      begin
+      AddUnit('markdown.elements');
+      AddUnit('markdown.utils');
+      AddUnit('markdown.render');
+      end;
+
     T:=P.Targets.AddUnit('markdown.fpdocrender.pas');
     with T.Dependencies do
       begin

+ 52 - 0
packages/fcl-md/src/markdown.htmlrender.pas

@@ -53,6 +53,9 @@ type
     Procedure RenderDocument(aDocument : TMarkDownDocument; aDest : TStrings); overload;
     procedure RenderChildren(aBlock : TMarkDownContainerBlock; aAppendNewLine : Boolean); overload;
     function RenderHTML(aDocument : TMarkDownDocument) : string;
+    procedure RenderHTMLToFile(aDocument : TMarkDownDocument; const aFileName : string);
+    class function FastRender(aDocument : TMarkDownDocument; aOptions : THTMLOptions; aTitle : String = ''; aHead : TStrings = Nil) : String;
+    class procedure FastRenderToFile(aDocument : TMarkDownDocument; const aFileName : string; aOptions : THTMLOptions; aTitle : String = ''; aHead : TStrings = Nil);
     Property HTML : String Read FHTML;
   published
     Property Options : THTMLOptions Read FOptions Write FOptions;
@@ -313,6 +316,55 @@ begin
   FHTML:='';
 end;
 
+procedure TMarkDownHTMLRenderer.RenderHTMLToFile(aDocument: TMarkDownDocument; const aFileName: string);
+var
+  lHTML : String;
+  lFile : THandle;
+begin
+  lHTML:=RenderHTML(aDocument);
+  lFile:=FileCreate(aFileName);
+  try
+    if lHTML<>'' then
+      FileWrite(lFile,lHTML[1],Length(lHTML)*SizeOf(Char));
+  finally
+    FileClose(lFile);
+  end;
+end;
+
+class function TMarkDownHTMLRenderer.FastRender(aDocument: TMarkDownDocument; aOptions: THTMLOptions; aTitle: String;
+  aHead: TStrings): String;
+var
+  lRender : TMarkDownHTMLRenderer;
+begin
+  lRender:=TMarkDownHTMLRenderer.Create(Nil);
+  try
+    lRender.Options:=aOptions;
+    lRender.Title:=aTitle;
+    if assigned(aHead) then
+      lRender.Head.Assign(aHead);
+    Result:=lRender.RenderHTML(aDocument);
+  finally
+    lRender.Free;
+  end;
+end;
+
+class procedure TMarkDownHTMLRenderer.FastRenderToFile(aDocument: TMarkDownDocument; const aFileName: string;
+  aOptions: THTMLOptions; aTitle: String; aHead: TStrings);
+var
+  lRender : TMarkDownHTMLRenderer;
+begin
+  lRender:=TMarkDownHTMLRenderer.Create(Nil);
+  try
+    lRender.Options:=aOptions;
+    lRender.Title:=aTitle;
+    if assigned(aHead) then
+      lRender.Head.Assign(aHead);
+    lRender.RenderHTMLToFile(aDocument,aFileName);
+  finally
+    lRender.Free;
+  end;
+end;
+
 
 procedure THTMLMarkDownTextRenderer.Append(const S: String);
 begin

+ 791 - 0
packages/fcl-md/src/markdown.latexrender.pas

@@ -0,0 +1,791 @@
+{
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 2025 by Michael Van Canneyt
+
+    Markdown LaTeX renderer.
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ *********************************************************************}
+
+unit MarkDown.LatexRender;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+{$IFDEF FPC_DOTTEDUNITS}
+  System.Classes, System.SysUtils, System.StrUtils, System.Contnrs, 
+{$ELSE}
+  Classes, SysUtils, strutils, contnrs, 
+{$ENDIF}  
+  MarkDown.Elements, 
+  MarkDown.Render, 
+  MarkDown.Utils;
+
+type
+  { TMarkDownLaTeXRenderer }
+  TLaTeXOption = (loEnvelope, loNumberedSections);
+  TLaTeXOptions = set of TLaTeXOption;
+
+  TMarkDownLaTeXRenderer = class(TMarkDownRenderer)
+  private
+    FBuilder: TStringBuilder;
+    FHead: TStrings;
+    FLaTeX: String;
+    FOptions: TLaTeXOptions;
+    FTitle: String;
+    FAuthor: String;
+    procedure SetHead(const aValue: TStrings);
+  Protected
+    Procedure Append(const aContent : String);
+    Procedure AppendNL(const aContent : String = '');
+    Property Builder : TStringBuilder Read FBuilder;
+  public
+    constructor Create(aOwner : TComponent); override;
+    destructor destroy; override;
+    Procedure RenderDocument(aDocument : TMarkDownDocument); override;overload;
+    Procedure RenderDocument(aDocument : TMarkDownDocument; aDest : TStrings); overload;
+    procedure RenderChildren(aBlock : TMarkDownContainerBlock; aAppendNewLine : Boolean); overload;
+    function RenderLaTeX(aDocument : TMarkDownDocument) : string;
+    function EscapeLaTeX(const S: String): String;
+  published
+    Property Options : TLaTeXOptions Read FOptions Write FOptions;
+    property Title : String Read FTitle Write FTitle;
+    property Author : String Read FAuthor Write FAuthor;
+    property Head : TStrings Read FHead Write SetHead;
+  end;
+
+  { TLaTeXMarkDownBlockRenderer }
+
+  TLaTeXMarkDownBlockRenderer = Class (TMarkDownBlockRenderer)
+  Private
+    function GetLaTeXRenderer: TMarkDownLaTeXRenderer;
+  protected
+    procedure Append(const S : String); inline;
+    procedure AppendNl(const S : String = ''); inline;
+    function HasOption(aOption : TLaTeXOption) : Boolean;
+    function Escape(const S: String): String;
+  public
+    property LaTeXRenderer : TMarkDownLaTeXRenderer Read GetLaTeXRenderer;
+  end;
+  TLaTeXMarkDownBlockRendererClass = class of TLaTeXMarkDownBlockRenderer;
+
+  { TLaTeXMarkDownTextRenderer }
+
+  TLaTeXMarkDownTextRenderer = class(TMarkDownTextRenderer)
+  Private
+    FStyleStack: Array of TNodeStyle;
+    FStyleStackLen : Integer;
+    FLastStyles : TNodeStyles;
+    function GetLaTeXRenderer: TMarkDownLaTeXRenderer;
+    function GetNodeTag(aElement: TMarkDownTextNode; Closing: Boolean): string;
+  protected
+    procedure PushStyle(aStyle : TNodeStyle);
+    function PopStyles(aStyle: TNodeStyles): TNodeStyle;
+    procedure PopStyle(aStyle : TNodeStyle);
+    procedure Append(const S : String); inline;
+    procedure DoRender(aElement: TMarkDownTextNode); override;
+    function Escape(const S: String): String;
+    procedure EmitStyleDiff(aStyles : TNodeStyles);
+  Public
+    procedure BeginBlock; override;
+    procedure EndBlock; override;
+    property LaTeXRenderer : TMarkDownLaTeXRenderer Read GetLaTeXRenderer;
+  end;
+  TLaTeXMarkDownTextRendererClass = class of TLaTeXMarkDownTextRenderer;
+
+  { TLaTeXParagraphBlockRenderer }
+
+  TLaTeXParagraphBlockRenderer = class (TLaTeXMarkDownBlockRenderer)
+  protected
+    procedure DoRender(aElement : TMarkDownBlock); override;
+  public
+    class function BlockClass : TMarkDownBlockClass; override;
+  end;
+
+  { TLaTeXMarkDownQuoteBlockRenderer }
+
+  TLaTeXMarkDownQuoteBlockRenderer = class(TLaTeXMarkDownBlockRenderer)
+  protected
+    procedure dorender(aElement : TMarkDownBlock); override;
+  public
+    class function BlockClass : TMarkDownBlockClass; override;
+  end;
+
+  { TLaTeXMarkDownTextBlockRenderer }
+
+  TLaTeXMarkDownTextBlockRenderer = class(TLaTeXMarkDownBlockRenderer)
+  protected
+    procedure DoRender(aElement : TMarkDownBlock); override;
+  public
+    class function BlockClass : TMarkDownBlockClass; override;
+  end;
+
+  { TLaTeXMarkDownListBlockRenderer }
+
+  TLaTeXMarkDownListBlockRenderer = class(TLaTeXMarkDownBlockRenderer)
+  protected
+    procedure Dorender(aElement : TMarkDownBlock); override;
+  public
+    class function BlockClass : TMarkDownBlockClass; override;
+  end;
+
+  { TLaTeXMarkDownListItemBlockRenderer }
+
+  TLaTeXMarkDownListItemBlockRenderer = class(TLaTeXMarkDownBlockRenderer)
+  protected
+    procedure Dorender(aElement : TMarkDownBlock); override;
+  public
+    class function BlockClass : TMarkDownBlockClass; override;
+  end;
+
+  { TLaTeXMarkDownCodeBlockRenderer }
+
+  TLaTeXMarkDownCodeBlockRenderer = class(TLaTeXMarkDownBlockRenderer)
+  protected
+    procedure Dorender(aElement : TMarkDownBlock); override;
+  public
+    class function BlockClass : TMarkDownBlockClass; override;
+  end;
+
+  { TLaTeXMarkDownHeadingBlockRenderer }
+
+  TLaTeXMarkDownHeadingBlockRenderer = class(TLaTeXMarkDownBlockRenderer)
+  protected
+    procedure Dorender(aElement : TMarkDownBlock); override;
+  public
+    class function BlockClass : TMarkDownBlockClass; override;
+  end;
+
+  { TLaTeXMarkDownThematicBreakBlockRenderer }
+
+  TLaTeXMarkDownThematicBreakBlockRenderer = class(TLaTeXMarkDownBlockRenderer)
+  protected
+    procedure Dorender(aElement : TMarkDownBlock); override;
+  public
+    class function BlockClass : TMarkDownBlockClass; override;
+  end;
+
+  { TLaTeXMarkDownTableBlockRenderer }
+
+  TLaTeXMarkDownTableBlockRenderer = class(TLaTeXMarkDownBlockRenderer)
+  protected
+    procedure Dorender(aElement : TMarkDownBlock); override;
+  public
+    class function BlockClass : TMarkDownBlockClass; override;
+  end;
+
+  { TLaTeXMarkDownTableRowBlockRenderer }
+
+  TLaTeXMarkDownTableRowBlockRenderer = class(TLaTeXMarkDownBlockRenderer)
+  protected
+    procedure Dorender(aElement : TMarkDownBlock); override;
+  public
+    class function BlockClass : TMarkDownBlockClass; override;
+  end;
+
+  { TLaTeXMarkDownDocumentRenderer }
+
+  TLaTeXMarkDownDocumentRenderer = class(TLaTeXMarkDownBlockRenderer)
+  protected
+    procedure Dorender(aElement : TMarkDownBlock); override;
+  public
+    class function BlockClass : TMarkDownBlockClass; override;
+  end;
+
+
+implementation
+
+type
+
+  { TStringBuilderHelper }
+
+  TStringBuilderHelper = class helper for TAnsiStringBuilder
+    function Append(aAnsiString : Ansistring) : TAnsiStringBuilder;
+  end;
+
+
+function TStringBuilderHelper.Append(aAnsiString: Ansistring): TAnsiStringBuilder;
+begin
+  Result:=Inherited Append(aAnsiString,0,System.Length(aAnsistring))
+end;
+
+{ TLaTeXMarkDownBlockRenderer }
+
+function TLaTeXMarkDownBlockRenderer.GetLaTeXRenderer: TMarkDownLaTeXRenderer;
+begin
+  if Renderer is TMarkDownLaTeXRenderer then
+    Result:=TMarkDownLaTeXRenderer(Renderer)
+  else
+    Result:=Nil;
+end;
+
+procedure TLaTeXMarkDownBlockRenderer.Append(const S: String);
+begin
+  LaTeXRenderer.Append(S);
+end;
+
+procedure TLaTeXMarkDownBlockRenderer.AppendNl(const S: String);
+begin
+  LaTeXRenderer.AppendNL(S);
+end;
+
+function TLaTeXMarkDownBlockRenderer.HasOption(aOption: TLaTeXOption): Boolean;
+begin
+  Result:=(Self.Renderer is TMarkDownLaTeXRenderer);
+  if Result then
+    Result:=aOption in TMarkDownLaTeXRenderer(Renderer).Options;
+end;
+
+function TLaTeXMarkDownBlockRenderer.Escape(const S: String): String;
+begin
+  Result:=LaTeXRenderer.EscapeLaTeX(S);
+end;
+
+{ TMarkDownLaTeXRenderer }
+
+procedure TMarkDownLaTeXRenderer.SetHead(const aValue: TStrings);
+begin
+  if FHead=aValue then Exit;
+  FHead:=aValue;
+end;
+
+procedure TMarkDownLaTeXRenderer.Append(const aContent: String);
+begin
+  FBuilder.Append(aContent);
+end;
+
+procedure TMarkDownLaTeXRenderer.AppendNL(const aContent: String);
+begin
+  if aContent<>'' then
+    FBuilder.Append(aContent);
+  FBuilder.Append(sLineBreak);
+end;
+
+constructor TMarkDownLaTeXRenderer.Create(aOwner: TComponent);
+begin
+  inherited Create(aOwner);
+  FHead:=TStringList.Create;
+end;
+
+destructor TMarkDownLaTeXRenderer.destroy;
+begin
+  FreeAndNil(FHead);
+  inherited destroy;
+end;
+
+procedure TMarkDownLaTeXRenderer.RenderDocument(aDocument: TMarkDownDocument);
+begin
+  FBuilder:=TStringBuilder.Create;
+  try
+    RenderBlock(aDocument);
+    FLaTeX:=FBuilder.ToString;
+  finally
+    FreeAndNil(FBuilder);
+  end;
+end;
+
+procedure TMarkDownLaTeXRenderer.RenderDocument(aDocument: TMarkDownDocument; aDest: TStrings);
+begin
+  aDest.Text:=RenderLaTeX(aDocument);
+end;
+
+procedure TMarkDownLaTeXRenderer.RenderChildren(aBlock: TMarkDownContainerBlock; aAppendNewLine: Boolean);
+var
+  i,iMax : integer;
+begin
+  iMax:=aBlock.Blocks.Count-1;
+  for I:=0 to iMax do
+    begin
+    if aAppendNewLine and (I>0) then
+      AppendNl();
+    RenderBlock(aBlock.Blocks[I]);
+    end;
+end;
+
+function TMarkDownLaTeXRenderer.RenderLaTeX(aDocument: TMarkDownDocument): string;
+begin
+  RenderDocument(aDocument);
+  Result:=FLaTeX;
+  FLaTeX:='';
+end;
+
+function TMarkDownLaTeXRenderer.EscapeLaTeX(const S: String): String;
+var
+  i: Integer;
+  c: Char;
+begin
+  Result := '';
+  for i := 1 to Length(S) do
+  begin
+    c := S[i];
+    case c of
+      '\': Result := Result + '\textbackslash{}';
+      '{': Result := Result + '\{';
+      '}': Result := Result + '\}';
+      '$': Result := Result + '\$';
+      '&': Result := Result + '\&';
+      '#': Result := Result + '\#';
+      '^': Result := Result + '\textasciicircum{}';
+      '_': Result := Result + '\_';
+      '%': Result := Result + '\%';
+      '~': Result := Result + '\textasciitilde{}';
+    else
+      Result := Result + c;
+    end;
+  end;
+end;
+
+{ TLaTeXMarkDownTextRenderer }
+
+procedure TLaTeXMarkDownTextRenderer.Append(const S: String);
+begin
+  LaTeXRenderer.Append(S);
+end;
+
+function TLaTeXMarkDownTextRenderer.Escape(const S: String): String;
+begin
+  Result:=LaTeXRenderer.EscapeLaTeX(S);
+end;
+
+function TLaTeXMarkDownTextRenderer.GetLaTeXRenderer: TMarkDownLaTeXRenderer;
+begin
+  if Renderer is TMarkDownLaTeXRenderer then
+    Result:=TMarkDownLaTeXRenderer(Renderer)
+  else
+    Result:=Nil;
+end;
+
+function TLaTeXMarkDownTextRenderer.GetNodeTag(aElement: TMarkDownTextNode; Closing: Boolean): string;
+var
+  lUrl: String;
+begin
+  Result := '';
+  case aElement.Kind of
+    nkCode:
+      if Closing then Result := '}' else Result := '\texttt{';
+    nkURI, nkEmail:
+      begin
+        lUrl := '';
+        if aElement.HasAttrs then
+          aElement.Attrs.TryGet('href', lUrl);
+          
+        if Closing then 
+          Result := '}' 
+        else 
+          Result := '\href{' + lUrl + '}{';
+      end;
+    nkImg:
+      begin
+        lUrl := '';
+        if aElement.HasAttrs then
+          aElement.Attrs.TryGet('src', lUrl);
+
+        if Closing then 
+          Result := '}' 
+        else 
+          Result := '\includegraphics{' + lUrl + '}{';
+      end;
+  end;
+end;
+
+procedure TLaTeXMarkDownTextRenderer.PushStyle(aStyle: TNodeStyle);
+begin
+  case aStyle of
+    nsStrong: Append('\textbf{');
+    nsEmph: Append('\textit{');
+    nsDelete: Append('\sout{'); // Requires ulem package
+  end;
+  if FStyleStackLen=Length(FStyleStack) then
+    SetLength(FStyleStack,FStyleStackLen+3);
+  FStyleStack[FStyleStackLen]:=aStyle;
+  Inc(FStyleStackLen);
+end;
+
+function TLaTeXMarkDownTextRenderer.Popstyles(aStyle: TNodeStyles) : TNodeStyle;
+begin
+  if (FStyleStackLen>0) and (FStyleStack[FStyleStackLen-1] in aStyle) then
+    begin
+    Result:=FStyleStack[FStyleStackLen-1];
+    Append('}');
+    Dec(FStyleStackLen);
+    end;
+end;
+
+procedure TLaTeXMarkDownTextRenderer.PopStyle(aStyle: TNodeStyle);
+begin
+  if (FStyleStackLen>0) and (FStyleStack[FStyleStackLen-1]=aStyle) then
+    begin
+    Append('}');
+    Dec(FStyleStackLen);
+    end;
+end;
+
+procedure TLaTeXMarkDownTextRenderer.EmitStyleDiff(aStyles : TNodeStyles);
+var
+  lRemove : TNodeStyles;
+  lAdd : TNodeStyles;
+  S : TNodeStyle;
+begin
+  lRemove:=[];
+  lAdd:=[];
+  For S in TNodeStyle do
+    begin
+    if (S in Self.FLastStyles) and Not (S in aStyles) then
+      Include(lRemove,S);
+    if (S in aStyles) and Not (S in Self.FLastStyles) then
+      Include(lAdd,S);
+    end;
+  While lRemove<>[] do
+    begin
+    S:=Self.PopStyles(lRemove);
+    Exclude(lRemove,S);
+    end;
+  For S in TNodeStyle do
+    if S in lAdd then
+      Self.PushStyle(S);
+  Self.FLastStyles:=aStyles;
+end;
+
+procedure TLaTeXMarkDownTextRenderer.DoRender(aElement: TMarkDownTextNode);
+var
+  lTag : string;
+begin
+  Self.EmitStyleDiff(aElement.Styles);
+  if aElement.Kind <> nkText then
+  begin
+    lTag := Self.GetNodeTag(aElement, False);
+    Append(lTag);
+  end;
+
+  if aElement.Kind = nkImg then
+  begin
+     // Img handling logic here...
+  end;
+
+  if aElement.NodeText<>'' then
+    Append(Self.Escape(aElement.NodeText));
+
+  if aElement.Kind <> nkText then
+  begin
+    lTag := Self.GetNodeTag(aElement, True);
+    Append(lTag);
+  end;
+  
+  aElement.Active:=False;
+end;
+
+procedure TLaTeXMarkDownTextRenderer.BeginBlock;
+begin
+  inherited BeginBlock;
+  Self.FStyleStackLen:=0;
+  Self.FLastStyles:=[];
+end;
+
+procedure TLaTeXMarkDownTextRenderer.EndBlock;
+begin
+  While (Self.FStyleStackLen>0) do
+    Self.Popstyle(Self.FStyleStack[Self.FStyleStackLen-1]);
+  Self.FLastStyles:=[];
+  inherited EndBlock;
+end;
+
+{ TLaTeXParagraphBlockRenderer }
+
+procedure TLaTeXParagraphBlockRenderer.DoRender(aElement: TMarkDownBlock);
+var
+  lNode : TMarkDownParagraphBlock absolute aElement;
+begin
+  // LaTeX paragraphs are separated by blank lines.
+  // No special environment needed usually, unless we want to enforce spacing.
+  Renderer.RenderChildren(lNode);
+  AppendNl; // Blank line after paragraph
+  AppendNl;
+end;
+
+class function TLaTeXParagraphBlockRenderer.BlockClass: TMarkDownBlockClass;
+begin
+  Result:=TMarkDownParagraphBlock;
+end;
+
+{ TLaTeXMarkDownTextBlockRenderer }
+
+class function TLaTeXMarkDownTextBlockRenderer.BlockClass: TMarkDownBlockClass;
+begin
+  Result:=TMarkDownTextBlock;
+end;
+
+procedure TLaTeXMarkDownTextBlockRenderer.DoRender(aElement: TMarkDownBlock);
+var
+  lNode : TMarkDownTextBlock absolute aElement;
+begin
+  if assigned(lNode) and assigned(lNode.Nodes) then
+    Renderer.RenderTextNodes(lNode.Nodes);
+end;
+
+{ TLaTeXMarkDownQuoteBlockRenderer }
+
+procedure TLaTeXMarkDownQuoteBlockRenderer.dorender(aElement: TMarkDownBlock);
+var
+  lNode : TMarkdownQuoteBlock absolute aElement;
+begin
+  AppendNl('\begin{quote}');
+  Renderer.RenderChildren(lNode);
+  AppendNl('\end{quote}');
+end;
+
+class function TLaTeXMarkDownQuoteBlockRenderer.BlockClass: TMarkDownBlockClass;
+begin
+  Result:=TMarkDownQuoteBlock;
+end;
+
+{ TLaTeXMarkDownListBlockRenderer }
+
+procedure TLaTeXMarkDownListBlockRenderer.Dorender(aElement : TMarkDownBlock);
+var
+  lNode : TMarkDownListBlock absolute aElement;
+begin
+  if not lNode.Ordered then
+    AppendNl('\begin{itemize}')
+  else
+    AppendNl('\begin{enumerate}');
+    
+  Renderer.RenderChildren(lNode);
+  
+  if lNode.Ordered then
+    AppendNl('\end{enumerate}')
+  else
+    AppendNl('\end{itemize}');
+end;
+
+class function TLaTeXMarkDownListBlockRenderer.BlockClass: TMarkDownBlockClass;
+begin
+  Result:=TMarkDownListBlock;
+end;
+
+
+{ TLaTeXMarkDownListItemBlockRenderer }
+
+procedure TLaTeXMarkDownListItemBlockRenderer.Dorender(aElement : TMarkDownBlock);
+var
+  lItemBlock : TMarkDownListItemBlock absolute aElement;
+  lBlock : TMarkDownBlock;
+  lPar : TMarkDownParagraphBlock absolute lBlock;
+  
+  function IsPlainBlock(aBlock : TMarkDownBlock) : boolean;
+  begin
+    Result:=(aBlock is TMarkDownParagraphBlock)
+             and (aBlock as TMarkDownParagraphBlock).isPlainPara
+             and not (lItemblock.parent as TMarkDownListBlock).loose
+  end;
+
+begin
+  Append('\item ');
+  For lBlock in lItemBlock.Blocks do
+    if IsPlainBlock(lBlock) then
+      LaTeXRenderer.RenderChildren(lPar,True)
+    else
+      Renderer.RenderBlock(lBlock);
+  AppendNl;
+end;
+
+class function TLaTeXMarkDownListItemBlockRenderer.BlockClass: TMarkDownBlockClass;
+begin
+  Result:=TMarkDownListItemBlock;
+end;
+
+{ TLaTeXMarkDownCodeBlockRenderer }
+
+procedure TLaTeXMarkDownCodeBlockRenderer.Dorender(aElement : TMarkDownBlock);
+var
+  lNode : TMarkDownCodeBlock absolute aElement;
+  lBlock : TMarkDownBlock;
+begin
+  AppendNl('\begin{verbatim}');
+  for lBlock in LNode.Blocks do
+    begin
+    Renderer.RenderCodeBlock(LBlock,lNode.Lang);
+    AppendNl;
+    end;
+  AppendNl('\end{verbatim}');
+end;
+
+class function TLaTeXMarkDownCodeBlockRenderer.BlockClass: TMarkDownBlockClass;
+begin
+  Result:=TMarkDownCodeBlock;
+end;
+
+{ TLaTeXMarkDownThematicBreakBlockRenderer }
+
+procedure TLaTeXMarkDownThematicBreakBlockRenderer.Dorender(aElement : TMarkDownBlock);
+begin
+  if Not Assigned(aElement) then
+    exit;
+  AppendNl('\hrule');
+end;
+
+class function TLaTeXMarkDownThematicBreakBlockRenderer.BlockClass: TMarkDownBlockClass;
+begin
+  Result:=TMarkDownThematicBreakBlock;
+end;
+
+{ TLaTeXMarkDownTableBlockRenderer }
+
+procedure TLaTeXMarkDownTableBlockRenderer.Dorender(aElement: TMarkDownBlock);
+var
+  lNode : TMarkDownTableBlock absolute aElement;
+  i : integer;
+  lCols: String;
+  c: TCellAlign;
+begin
+  // Construct column definition
+  lCols := '';
+  for c in lNode.Columns do
+  begin
+    case c of
+      caLeft: lCols := lCols + 'l|';
+      caRight: lCols := lCols + 'r|';
+      caCenter: lCols := lCols + 'c|';
+    end;
+  end;
+  if Length(lCols) > 0 then
+    lCols := '|' + lCols;
+
+  AppendNl('\begin{tabular}{' + lCols + '}');
+  AppendNl('\hline');
+  
+  // Header
+  Renderer.RenderBlock(lNode.blocks[0]);
+  AppendNl('\hline');
+  
+  if lNode.blocks.Count > 1 then
+  begin
+    for i := 1 to lNode.blocks.Count -1  do
+      Renderer.RenderBlock(lnode.blocks[i]);
+    AppendNl('\hline');
+  end;
+  AppendNl('\end{tabular}');
+end;
+
+class function TLaTeXMarkDownTableBlockRenderer.BlockClass: TMarkDownBlockClass;
+begin
+  Result:=TMarkDownTableBlock;
+end;
+
+{ TLaTeXMarkDownTableRowBlockRenderer }
+
+procedure TLaTeXMarkDownTableRowBlockRenderer.Dorender(aElement : TMarkDownBlock);
+var
+  lNode : TMarkDownTableRowBlock absolute aElement;
+  i, lCount : integer;
+begin
+  lCount:=lNode.blocks.Count;
+  for i:=0 to lCount-1 do
+    begin
+    if i > 0 then Append(' & ');
+    Renderer.RenderBlock(lNode.blocks[i]);
+    end;
+  AppendNl(' \');
+end;
+
+class function TLaTeXMarkDownTableRowBlockRenderer.BlockClass: TMarkDownBlockClass;
+begin
+  Result:=TMarkDownTableRowBlock;
+end;
+
+{ TLaTeXMarkDownHeadingBlockRenderer }
+
+procedure TLaTeXMarkDownHeadingBlockRenderer.Dorender(aElement : TMarkDownBlock);
+var
+  lNode : TMarkDownHeadingBlock absolute aElement;
+  lSection: String;
+  lNumbered: Boolean;
+begin
+  lNumbered := HasOption(loNumberedSections);
+  case lNode.Level of
+    1: lSection := 'section';
+    2: lSection := 'subsection';
+    3: lSection := 'subsubsection';
+    4: lSection := 'paragraph';
+    5: lSection := 'subparagraph';
+    else lSection := 'textbf'; // Fallback
+  end;
+  
+  if not lNumbered then
+    lSection := lSection + '*';
+
+  Append('\' + lSection + '{');
+  Renderer.RenderChildren(lNode);
+  Append('}');
+  AppendNl;
+end;
+
+class function TLaTeXMarkDownHeadingBlockRenderer.BlockClass: TMarkDownBlockClass;
+begin
+  Result:=TMarkDownHeadingBlock;
+end;
+
+
+{ TLaTeXMarkDownDocumentRenderer }
+
+procedure TLaTeXMarkDownDocumentRenderer.Dorender(aElement: TMarkDownBlock);
+var
+  H : String;
+begin
+  if HasOption(loEnvelope) then
+    begin
+    AppendNL('\documentclass{article}');
+    AppendNL('\usepackage[utf8]{inputenc}');
+    AppendNL('\usepackage{graphicx}');
+    AppendNL('\usepackage{hyperref}');
+    AppendNL('\usepackage{ulem}'); // For strikethrough
+    
+    if LaTeXRenderer.Title<>'' then
+      AppendNL('\title{' + LaTeXRenderer.EscapeLaTeX(LaTeXRenderer.Title) + '}');
+    if LaTeXRenderer.Author<>'' then
+      AppendNL('\author{' + LaTeXRenderer.EscapeLaTeX(LaTeXRenderer.Author) + '}');
+      
+    for H in LaTeXRenderer.Head do
+      AppendNL(H);
+      
+    AppendNL('\begin{document}');
+    
+    if LaTeXRenderer.Title<>'' then
+      AppendNL('\maketitle');
+    end;
+    
+  Renderer.RenderChildren(aElement as TMarkDownDocument);
+  
+  if HasOption(loEnvelope) then
+    begin
+    AppendNL('\end{document}');
+    end;
+end;
+
+class function TLaTeXMarkDownDocumentRenderer.BlockClass: TMarkDownBlockClass;
+begin
+  Result:=TMarkDownDocument
+end;
+
+
+initialization
+  TLaTeXMarkDownHeadingBlockRenderer.RegisterRenderer(TMarkDownLaTeXRenderer);
+  TLaTeXParagraphBlockRenderer.RegisterRenderer(TMarkDownLaTeXRenderer);
+  TLaTeXMarkDownQuoteBlockRenderer.RegisterRenderer(TMarkDownLaTeXRenderer);
+  TLaTeXMarkDownTextBlockRenderer.RegisterRenderer(TMarkDownLaTeXRenderer);
+  TLaTeXMarkDownListBlockRenderer.RegisterRenderer(TMarkDownLaTeXRenderer);
+  TLaTeXMarkDownListItemBlockRenderer.RegisterRenderer(TMarkDownLaTeXRenderer);
+  TLaTeXMarkDownCodeBlockRenderer.RegisterRenderer(TMarkDownLaTeXRenderer);
+  TLaTeXMarkDownThematicBreakBlockRenderer.RegisterRenderer(TMarkDownLaTeXRenderer);
+  TLaTeXMarkDownTableBlockRenderer.RegisterRenderer(TMarkDownLaTeXRenderer);
+  TLaTeXMarkDownTableRowBlockRenderer.RegisterRenderer(TMarkDownLaTeXRenderer);
+  TLaTeXMarkDownDocumentRenderer.RegisterRenderer(TMarkDownLaTeXRenderer);
+  TLaTeXMarkDownTextRenderer.RegisterRenderer(TMarkDownLaTeXRenderer);
+end.

+ 30 - 0
packages/fcl-md/src/markdown.parser.pas

@@ -158,10 +158,14 @@ type
     procedure ParseInline(aParent : TMarkDownContainerBlock; const aLine : String);
     // Parse the markDown in strings
     function Parse(aSource: TStrings): TMarkDownDocument; overload;
+    // Utility function: Parse the markDown in file aFileName.
+    function ParseFile(const aFilename : string): TMarkDownDocument;
     // Helper : is the last block a plain paragraph ?
     class function InPara(blocks : TMarkDownBlockList; canBeQuote : boolean) : boolean;
     // Helper to quickly parse a stringlist into a markdown document
     class function FastParse(aSource: TStrings; aOptions: TMarkDownOptions): TMarkDownDocument;
+    // Helper to quickly parse a stringlist into a markdown document
+    class function FastParseFile(const aFileName : string; aOptions: TMarkDownOptions): TMarkDownDocument;
     // State control in lazy continuation .
     property Lazy : Boolean Read FLazy Write FLazy;
     // HTML entities to convert
@@ -348,6 +352,19 @@ begin
   end;
 end;
 
+class function TMarkDownParser.FastParseFile(const aFileName: string; aOptions: TMarkDownOptions): TMarkDownDocument;
+var
+  lFile : TStrings;
+begin
+  lFile:=TStringList.Create;
+  try
+    lFile.LoadFromFile(aFileName,TEncoding.UTF8);
+    Result:=FastParse(lFile,aOptions);
+  finally
+    lFile.Free;
+  end;
+end;
+
 
 procedure TMarkDownParser.CollectEntities(aList :TFPStringHashTable);
 
@@ -424,6 +441,19 @@ begin
   end;
 end;
 
+function TMarkDownParser.ParseFile(const aFilename: string): TMarkDownDocument;
+var
+  lFile : TStrings;
+begin
+  lFile:=TStringList.Create;
+  try
+    lFile.LoadFromFile(aFileName,TEncoding.UTF8);
+    Result:=Parse(lFile);
+  finally
+    lFile.Free;
+  end;
+end;
+
 
 function TMarkDownParser.NextLine: TMarkDownLine;
 

+ 10 - 0
packages/fcl-md/tests/testmd.lpi

@@ -121,6 +121,16 @@
         <IsPartOfProject Value="True"/>
         <UnitName Value="Markdown.FPDocRender"/>
       </Unit>
+      <Unit>
+        <Filename Value="../src/markdown.latexrender.pas"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="MarkDown.LatexRender"/>
+      </Unit>
+      <Unit>
+        <Filename Value="utest.markdown.latexrender.pas"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="UTest.Markdown.LaTeXRender"/>
+      </Unit>
     </Units>
   </ProjectOptions>
   <CompilerOptions>

+ 1 - 1
packages/fcl-md/tests/testmd.lpr

@@ -6,7 +6,7 @@ uses
   cwstring,Classes, consoletestrunner, utest.markdown.utils, markdown.elements, Markdown.HTMLEntities,
   markdown.htmlrender, markdown.inlinetext, markdown.line, markdown.parser, markdown.render, markdown.scanner,
   markdown.utils, utest.markdown.scanner, utest.markdown.inlinetext, utest.markdown.htmlrender, utest.markdown.parser,
-  utest.markdown.fpdocrender,markdown.processors;
+  utest.markdown.fpdocrender,markdown.latexrender,utest.markdown.latexrender,markdown.processors;
 
 type
 

+ 352 - 0
packages/fcl-md/tests/utest.markdown.latexrender.pas

@@ -0,0 +1,352 @@
+{
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 2025 by Michael Van Canneyt
+
+    Markdown LaTeX renderer tests
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ *********************************************************************}
+unit UTest.Markdown.LaTeXRender;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, fpcunit, testregistry,
+  MarkDown.Elements,
+  MarkDown.LatexRender;
+
+type
+
+  { TTestLaTeXRender }
+
+  TTestLaTeXRender = Class(TTestCase)
+  private
+    FLaTeXRenderer : TMarkDownLaTeXRenderer;
+    FDocument: TMarkDownDocument;
+  Public
+    Procedure SetUp; override;
+    Procedure TearDown; override;
+    function CreateTextBlock(aParent: TMarkdownBlock; const aText,aTextNode: string; aNodeStyle : TNodeStyles=[]): TMarkDownTextBlock;
+    function CreateParagraphBlock(const aTextNode: string): TMarkdownBlock;
+    function CreateQuotedBlock(const aTextNode: string): TMarkdownBlock;
+    function CreateHeadingBlock(const aTextNode: string; aLevel : integer): TMarkdownBlock;
+    function CreateListBlock(aOrdered : boolean; const aListItemText : string): TMarkDownListBlock;
+    function CreateListItemBlock(aParent: TMarkDownContainerBlock; const aText: string): TMarkDownListItemBlock;
+    function AppendTextNode(aBlock: TMarkDownTextBlock; const aText: string; aNodeStyle : TNodeStyles) : TMarkDownTextNode;
+    procedure TestRender(const aLaTeX : string);
+    Property Renderer : TMarkDownLaTeXRenderer Read FLaTeXRenderer;
+    Property Document : TMarkDownDocument Read FDocument;
+  Published
+    procedure TestHookup;
+    procedure TestEmpty;
+    procedure TestEmptyNoEnvelope;
+    procedure TestEmptyTitle;
+    procedure TestTextBlockEmpty;
+    procedure TestTextBlockText;
+    procedure TestTextBlockTextEscaping;
+    procedure TestTextBlockTextStrong;
+    procedure TestTextBlockTextEmph;
+    procedure TestTextBlockTextDelete;
+    procedure TestTextBlockTextStrongEmph;
+    procedure TestPragraphBlockEmpty;
+    procedure TestPragraphBlockText;
+    procedure TestQuotedBlockEmpty;
+    procedure TestQuotedBlockText;
+    procedure TestHeadingBlockEmpty;
+    procedure TestHeadingBlockText;
+    procedure TestHeadingBlockTextLevel2;
+    procedure TestUnorderedListEmpty;
+    procedure TestUnorderedListOneItem;
+  end;
+
+implementation
+
+{ TTestLaTeXRender }
+
+procedure TTestLaTeXRender.SetUp;
+
+begin
+  FLaTeXRenderer:=TMarkDownLaTeXRenderer.Create(Nil);
+  FDocument:=TMarkDownDocument.Create(Nil,1);
+end;
+
+
+procedure TTestLaTeXRender.TearDown;
+
+begin
+  FreeAndNil(FDocument);
+  FreeAndNil(FLaTeXRenderer);
+end;
+
+
+function TTestLaTeXRender.CreateTextBlock(aParent: TMarkdownBlock; const aText, aTextNode: string; aNodeStyle: TNodeStyles): TMarkDownTextBlock;
+
+begin
+  Result:=TMarkDownTextBlock.Create(aParent,1,aText);
+  if aTextNode<>'' then
+    AppendTextNode(Result,aTextNode,aNodeStyle);
+end;
+
+function TTestLaTeXRender.CreateParagraphBlock(const aTextNode: string): TMarkdownBlock;
+
+begin
+  Result:=TMarkDownParagraphBlock.Create(FDocument,1);
+  if aTextNode<>'' then
+    CreateTextBlock(Result,aTextNode,aTextNode);
+end;
+
+function TTestLaTeXRender.CreateQuotedBlock(const aTextNode: string): TMarkdownBlock;
+
+begin
+  Result:=TMarkDownQuoteBlock.Create(FDocument,1);
+  if aTextNode<>'' then
+    CreateTextBlock(Result,aTextNode,aTextNode);
+end;
+
+function TTestLaTeXRender.CreateHeadingBlock(const aTextNode: string; aLevel: integer): TMarkdownBlock;
+
+begin
+  Result:=TMarkDownHeadingBlock.Create(FDocument,1,aLevel);
+  if aTextNode<>'' then
+    CreateTextBlock(Result,aTextNode,aTextNode);
+end;
+
+function TTestLaTeXRender.CreateListItemBlock(aParent: TMarkDownContainerBlock; const aText: string): TMarkDownListItemBlock;
+
+var
+  lPar : TMarkDownParagraphBlock;
+begin
+  Result:=TMarkDownListItemBlock.Create(aParent,1);
+  lPar:=TMarkDownParagraphBlock.Create(Result,1);
+  CreateTextBlock(lPar,'',aText);
+end;
+
+
+function TTestLaTeXRender.CreateListBlock(aOrdered: boolean; const aListItemText: string): TMarkDownListBlock;
+
+begin
+  Result:=TMarkDownListBlock.Create(FDocument,1);
+  Result.ordered:=aOrdered;
+  if aListItemText<>'' then
+    CreateListItemBlock(Result,aListItemText);
+end;
+
+function TTestLaTeXRender.AppendTextNode(aBlock: TMarkDownTextBlock; const aText: string; aNodeStyle: TNodeStyles): TMarkDownTextNode;
+
+var
+  p : TPosition;
+  t : TMarkdownTextNode;
+
+begin
+  if aBlock.Nodes=Nil then
+    aBlock.Nodes:=TMarkDownTextNodeList.Create(True);
+  p.col:=Length(aBlock.Text);
+  p.line:=1;
+  t:=TMarkDownTextNode.Create(p,nkText);
+  t.addText(aText);
+  t.active:=False;
+  T.Styles:=aNodeStyle;
+  aBlock.Nodes.Add(t);
+  Result:=T;
+end;
+
+procedure TTestLaTeXRender.TestRender(const aLaTeX: string);
+
+var
+  L : TStrings;
+
+begin
+  L:=TstringList.Create;
+  try
+    L.SkipLastLineBreak:=True;
+    Renderer.RenderDocument(FDocument,L);
+    assertEquals('Correct latex: ',aLaTeX,L.Text);
+  finally
+    L.Free;
+  end;
+end;
+
+
+procedure TTestLaTeXRender.TestHookup;
+
+begin
+  AssertNotNull('Have renderer',FLaTeXRenderer);
+  AssertNotNull('Have document',FDocument);
+  AssertEquals('Have empty document',0,FDocument.blocks.Count);
+end;
+
+
+procedure TTestLaTeXRender.TestEmpty;
+
+begin
+  Renderer.Options:=[loEnvelope];
+  TestRender('\documentclass{article}'+sLineBreak+
+             '\usepackage[utf8]{inputenc}'+sLineBreak+
+             '\usepackage{graphicx}'+sLineBreak+
+             '\usepackage{hyperref}'+sLineBreak+
+             '\usepackage{ulem}'+sLineBreak+
+             '\begin{document}'+sLineBreak+
+             '\end{document}');
+end;
+
+
+procedure TTestLaTeXRender.TestEmptyNoEnvelope;
+
+begin
+  Renderer.Options:=[];
+  TestRender('');
+end;
+
+
+procedure TTestLaTeXRender.TestEmptyTitle;
+
+begin
+  Renderer.Options:=[loEnvelope];
+  Renderer.Title:='a';
+  TestRender('\documentclass{article}'+sLineBreak+
+             '\usepackage[utf8]{inputenc}'+sLineBreak+
+             '\usepackage{graphicx}'+sLineBreak+
+             '\usepackage{hyperref}'+sLineBreak+
+             '\usepackage{ulem}'+sLineBreak+
+             '\title{a}'+sLineBreak+
+             '\begin{document}'+sLineBreak+
+             '\maketitle'+sLineBreak+
+             '\end{document}');
+end;
+
+
+procedure TTestLaTeXRender.TestTextBlockEmpty;
+
+begin
+  CreateTextBlock(Document,'a','');
+  TestRender('');
+end;
+
+
+procedure TTestLaTeXRender.TestTextBlockText;
+
+begin
+  CreateTextBlock(Document,'a','a');
+  TestRender('a');
+end;
+
+procedure TTestLaTeXRender.TestTextBlockTextEscaping;
+
+begin
+  CreateTextBlock(Document,'a','# $ % ^ & _ { } ~ \');
+  // Expected: \# \$ \% \textasciicircum{} \& \_ \{ \} \textasciitilde{} \textbackslash{}
+  TestRender('\# \$ \% \textasciicircum{} \& \_ \{ \} \textasciitilde{} \textbackslash{}');
+end;
+
+
+procedure TTestLaTeXRender.TestTextBlockTextStrong;
+
+begin
+  CreateTextBlock(Document,'a','a',[nsStrong]);
+  TestRender('\textbf{a}');
+end;
+
+
+procedure TTestLaTeXRender.TestTextBlockTextEmph;
+
+begin
+  CreateTextBlock(Document,'a','a',[nsEmph]);
+  TestRender('\textit{a}');
+end;
+
+
+procedure TTestLaTeXRender.TestTextBlockTextDelete;
+
+begin
+  CreateTextBlock(Document,'a','a',[nsDelete]);
+  TestRender('\sout{a}');
+end;
+
+
+procedure TTestLaTeXRender.TestTextBlockTextStrongEmph;
+
+begin
+  CreateTextBlock(Document,'a','a',[nsStrong,nsEmph]);
+  TestRender('\textbf{\textit{a}}');
+end;
+
+
+procedure TTestLaTeXRender.TestPragraphBlockEmpty;
+
+begin
+  CreateParagraphBlock('');
+  TestRender(sLineBreak); // Blank line
+end;
+
+procedure TTestLaTeXRender.TestQuotedBlockEmpty;
+
+begin
+  CreateQuotedBlock('');
+  TestRender('\begin{quote}'+sLineBreak+'\end{quote}');
+end;
+
+procedure TTestLaTeXRender.TestUnorderedListEmpty;
+
+begin
+  CreateListBlock(false,'');
+  TestRender('\begin{itemize}'+sLineBreak+'\end{itemize}');
+end;
+
+procedure TTestLaTeXRender.TestPragraphBlockText;
+
+begin
+  CreateParagraphBlock('a');
+  TestRender('a'+sLineBreak);
+end;
+
+procedure TTestLaTeXRender.TestQuotedBlockText;
+
+begin
+  CreateQuotedBlock('a');
+  TestRender('\begin{quote}'+sLineBreak+'a\end{quote}');
+end;
+
+procedure TTestLaTeXRender.TestHeadingBlockEmpty;
+
+begin
+  CreateHeadingBlock('',1);
+  TestRender('\section*{}');
+end;
+
+procedure TTestLaTeXRender.TestHeadingBlockText;
+
+begin
+  CreateHeadingBlock('a',1);
+  TestRender('\section*{a}');
+end;
+
+procedure TTestLaTeXRender.TestHeadingBlockTextLevel2;
+
+begin
+  CreateHeadingBlock('a',2);
+  TestRender('\subsection*{a}');
+end;
+
+procedure TTestLaTeXRender.TestUnorderedListOneItem;
+
+begin
+  CreateListBlock(false,'a');
+  // ListItem appends '\item ' then renders children.
+  // Children = Paragraph 'a'. Plain paragraph renders children (text 'a').
+  // Item renderer now adds a newline.
+  // List renderer adds \begin{itemize}\n ... \end{itemize}\n
+  TestRender('\begin{itemize}'+sLineBreak+'\item a'+sLineBreak+'\end{itemize}');
+end;
+
+
+initialization
+  Registertest(TTestLaTeXRender);
+end.

+ 1 - 8
packages/fpmake.pp

@@ -4,14 +4,7 @@
 {$mode objfpc}{$H+}
 program fpmake;
 
-uses
-{$ifdef unix}
- cwstring,
-{$ifndef NO_THREADING}
- cthreads,
-{$endif}
-{$endif}
- sysutils, Classes, fpmkunit;
+uses {$ifdef unix}cwstring,cthreads,{$endif} sysutils, Classes, fpmkunit;
 
 Var
   TBuild,T : TTarget;

+ 66 - 0
packages/rtl-objpas/src/inc/cvarutil.inc

@@ -1400,6 +1400,72 @@ begin
 end;
 
 
+Function VariantToUnicodeString(const VargSrc : TVarData) : UnicodeString;
+begin
+  {$IFDEF DEBUG_VARUTILS} if __DEBUG_VARUTILS then begin
+  DumpVariant('VariantToUnicodeString', VargSrc);
+  end; {$ENDIF}
+
+  with VargSrc do
+    case vType and not varTypeMask of
+      0: case vType of
+        varEmpty    : Result := '';
+        varSmallInt : Result := IntToStr(vSmallInt);
+        varShortInt : Result := IntToStr(vShortInt);
+        varInteger  : Result := IntToStr(vInteger);
+{$ifndef FPUNONE}
+        varSingle   : Result := FloatToStr(vSingle);
+        varDouble   : Result := FloatToStr(vDouble);
+        varCurrency : Result := CurrToStr(vCurrency);
+        varDate     : Result := VarDateToString(vDate);
+{$endif}
+        varBoolean  : Result := BoolToStr(vBoolean, True);
+        varVariant  : Result := VariantToUnicodeString(PVarData(vPointer)^);
+        varByte     : Result := IntToStr(vByte);
+        varWord     : Result := IntToStr(vWord);
+        varLongWord : Result := IntToStr(vLongWord);
+        varInt64    : Result := IntToStr(vInt64);
+        varQword    : Result := IntToStr(vQWord);
+        varOleStr   : Result := WideString(Pointer(vOleStr));
+        varString   : Result := AnsiString(vString);
+        varUString  : Result := UnicodeString(vString);
+      else
+        VariantTypeMismatch(vType, varOleStr);
+      end;
+      varByRef: if Assigned(vPointer) then case vType and varTypeMask of
+        varSmallInt : Result := IntToStr(PSmallInt(vPointer)^);
+        varShortInt : Result := IntToStr(PShortInt(vPointer)^);
+        varInteger  : Result := IntToStr(PInteger(vPointer)^);
+{$ifndef FPUNONE}
+        varSingle   : Result := FloatToStr(PSingle(vPointer)^);
+        varDouble   : Result := FloatToStr(PDouble(vPointer)^);
+        varCurrency : Result := CurrToStr(PCurrency(vPointer)^);
+        varDate     : Result := VarDateToString(PDate(vPointer)^);
+{$endif}
+        varBoolean  : Result := BoolToStr(PWordBool(vPointer)^, True);
+        varVariant  : Result := VariantToUnicodeString(PVarData(vPointer)^);
+        varByte     : Result := IntToStr(PByte(vPointer)^);
+        varWord     : Result := IntToStr(PWord(vPointer)^);
+        varLongWord : Result := IntToStr(PLongWord(vPointer)^);
+        varInt64    : Result := IntToStr(PInt64(vPointer)^);
+        varQword    : Result := IntToStr(PQWord(vPointer)^);
+        varOleStr   : Result := WideString(PPointer(vPointer)^);
+        varString   : Result := AnsiString(PPointer(vPointer)^);
+        varUString  : Result := UnicodeString(PPointer(vPointer)^);
+      else { other vtype }
+        VariantTypeMismatch(vType, varUString);
+      end else { pointer is nil }
+        VariantTypeMismatch(vType, varUString);
+    else { array or something like that }
+      VariantTypeMismatch(vType, varUString);
+    end;
+
+  {$IFDEF DEBUG_VARUTILS} if __DEBUG_VARUTILS then begin
+  WriteLn('VariantToUnicodeString -> ', Result);
+  end; {$ENDIF}
+end;
+
+
 Function VariantToShortString(const VargSrc : TVarData) : ShortString;
 begin
   Result:=VariantToAnsiString(VargSrc);

+ 34 - 75
packages/rtl-objpas/src/inc/dateutil.inc

@@ -88,6 +88,7 @@ Const
   ApproxDaysPerMonth: Double = 30.4375;
   ApproxDaysPerYear: Double  = 365.25;
 
+  DateTimeEpsilon: Double = 1E-10;    // about 0.1 ms
 
 
 { ---------------------------------------------------------------------
@@ -654,7 +655,6 @@ uses
 {$ENDIF FPC_DOTTEDUNITS}
 
 const
-  TDateTimeEpsilon = 2.2204460493e-16;
   HalfMilliSecond = OneMillisecond /2 ;
   SErrLocalTimeInvalid = 'Invalid local time: %s';
 
@@ -1525,33 +1525,48 @@ end;
     Period functions.
   ---------------------------------------------------------------------}
 
-{
-  These functions are declared as approximate by Borland.
-  A bit strange, since it can be calculated exactly ?
-
-  -- No, because you need rounding or truncating (JM)
-}
+{ Converts a TDateTime variable to a linear number which can be used in
+  arithmetics, in particular for negative values.
 
+  TDateTime is not defined in the interval [-1.0..0.0[. Additionally, when
+  negative the time part must be treated using its absolute value (0.25 always
+  means "6 a.m."). And take care of rounding errors
 
-function DateTimeToNumber(ADateTime: TDateTime): Double;
+  Example:
+    Dec 29, 1899-12-29 6:00 corresponds to value -1.25, the "linear number" is -0.75 }
+    // Converts TDateTime to linear date number
+function DateTimeToNumber(ADateTime: TDateTime):double;
 begin
   if ADateTime >= 0  then
     Result := ADateTime
   else
-    Result := int(ADateTime) - frac(ADateTime);
+    Result := Trunc(ADateTime) - Frac(ADateTime);
 end;
 
-function NumberToDateTime(AValue: Double): TDateTime;
+function NumberToDateTime(AValue: double): TDateTime;
+var
+  f: Double;
+  d: Integer;
 begin
   if AValue >= 0 then
     Result := AValue
   else
-    Result := int(AValue) + frac(AValue);
+  begin
+    d := Trunc(AValue) - 1;
+    f := -Frac(AValue);
+    if (f < DateTimeEpsilon) then
+      f := -1.0
+    else if (1.0 - f < DateTimeEpsilon) then
+      f := 0.0
+    else
+      f := 1.0 - f;
+    Result := d - f;
+  end;
 end;
 
-Function DateTimeDiff(const ANow, AThen: TDateTime): TDateTime;
+Function DateTimeDiff(const ANow, AThen: TDateTime): Double;
 begin
-  Result := NumberToDateTime(DateTimeToNumber(ANow) - DateTimeToNumber(AThen));
+  Result := DateTimeToNumber(ANow) - DateTimeToNumber(AThen);
 end;
 
 Function YearsBetween(const ANow, AThen: TDateTime; AExact : Boolean = False): Integer;
@@ -1713,44 +1728,6 @@ end;
     Increment/decrement functions.
   ---------------------------------------------------------------------}
 
-{ TDateTime is not defined in the interval [-1.0..0.0[. Additionally, when
-  negative the time part must be treated using its absolute value (0.25 always
-  means "6 a.m.") -> skip the gap and convert the time part when crossing the
-  gap -- and take care of rounding errors }
-Procedure MaybeSkipTimeWarp(OldDate: TDateTime; var NewDate: TDateTime);
-begin
-  if (OldDate>=0) and (NewDate<-TDateTimeEpsilon) then
-    NewDate:=int(NewDate-1.0+TDateTimeEpsilon)-frac(1.0+frac(NewDate))
-  else if (OldDate<=-1.0) and (NewDate>-1.0+TDateTimeEpsilon) then
-    NewDate:=int(NewDate+1.0-TDateTimeEpsilon)+frac(1.0-abs(frac(1.0+NewDate)));
-end;
-
-
-function IncNegativeTime(AValue, Addend: TDateTime): TDateTime;
-var
-  newtime: tdatetime;
-begin
-  newtime:=-frac(Avalue)+frac(Addend);
-  { handle rounding errors }
-  if SameValue(newtime,int(newtime)+1,TDateTimeEpsilon) then
-    newtime:=int(newtime)+1
-  else if SameValue(newtime,int(newtime),TDateTimeEpsilon) then
-    newtime:=int(newtime);
-  { time underflow -> previous day }
-  if newtime<-TDateTimeEpsilon then
-    begin
-      newtime:=1.0+newtime;
-      avalue:=int(avalue)-1;
-    end
-  { time overflow -> next day }
-  else if newtime>=1.0-TDateTimeEpsilon then
-    begin
-      newtime:=newtime-1.0;
-      avalue:=int(avalue)+1;
-    end;
-  Result:=int(AValue)+int(Addend)-newtime;
-end;
-
 Function IncYear(const AValue: TDateTime; const ANumberOfYears: Integer ): TDateTime;
 
 Var
@@ -1772,8 +1749,7 @@ end;
 
 Function IncWeek(const AValue: TDateTime; const ANumberOfWeeks: Integer): TDateTime;
 begin
-  Result:=AValue+ANumberOfWeeks*7;
-  MaybeSkipTimeWarp(AValue,Result);
+  Result := NumberToDateTime(DateTimeToNumber(AValue) + ANumberOfWeeks * 7);
 end;
 
 
@@ -1785,8 +1761,7 @@ end;
 
 Function IncDay(const AValue: TDateTime; const ANumberOfDays: Integer): TDateTime;
 begin
-  Result:=AValue+ANumberOfDays;
-  MaybeSkipTimeWarp(AValue,Result);
+  Result := NumberToDateTime(DateTimeToNumber(AValue) + ANumberOfDays);
 end;
 
 
@@ -1798,11 +1773,7 @@ end;
 
 Function IncHour(const AValue: TDateTime; const ANumberOfHours: Int64): TDateTime;
 begin
-  if AValue>=0 then
-    Result:=AValue+ANumberOfHours/HoursPerDay
-  else
-    Result:=IncNegativeTime(Avalue,ANumberOfHours/HoursPerDay);
-  MaybeSkipTimeWarp(AValue,Result);
+  Result := NumberToDateTime(DateTimeToNumber(AValue) + ANumberOfHours/24);
 end;
 
 
@@ -1814,11 +1785,7 @@ end;
 
 Function IncMinute(const AValue: TDateTime; const ANumberOfMinutes: Int64): TDateTime;
 begin
-  if AValue>=0 then
-    Result:=AValue+ANumberOfMinutes/MinsPerDay
-  else
-    Result:=IncNegativeTime(Avalue,ANumberOfMinutes/MinsPerDay);
-  MaybeSkipTimeWarp(AValue,Result);
+  Result := NumberToDateTime(DateTimeToNumber(AValue) + ANumberOfMinutes/MinsPerDay);
 end;
 
 
@@ -1830,11 +1797,7 @@ end;
 
 Function IncSecond(const AValue: TDateTime; const ANumberOfSeconds: Int64): TDateTime;
 begin
-  if AValue>=0 then
-    Result:=AValue+ANumberOfSeconds/SecsPerDay
-  else
-    Result:=IncNegativeTime(Avalue,ANumberOfSeconds/SecsPerDay);
-  MaybeSkipTimeWarp(AValue,Result);
+  Result := NumberToDateTime(DateTimeToNumber(AValue) + ANumberOfSeconds/SecsPerDay);
 end;
 
 
@@ -1846,11 +1809,7 @@ end;
 
 Function IncMilliSecond(const AValue: TDateTime; const ANumberOfMilliSeconds: Int64): TDateTime;
 begin
-  if Avalue>=0 then
-    Result:=AValue+ANumberOfMilliSeconds/MSecsPerDay
-  else
-    Result:=IncNegativeTime(Avalue,ANumberOfMilliSeconds/MSecsPerDay);
-  MaybeSkipTimeWarp(AValue,Result);
+  Result := NumberToDateTime(DateTimeToNumber(AValue) + ANumberOfMilliSeconds/MSecsPerDay);
 end;
 
 

+ 25 - 10
packages/rtl-objpas/src/inc/rtti.pp

@@ -28,6 +28,20 @@ unit Rtti;
 {$goto on}
 {$Assertions on}
 
+{ Note: the Lazarus IDE might have problems to correctly handle some syntax
+        elements or to navigate to the invoke.inc if the main source is
+        navigated inside the IDE; to allow ensure that the InLazIDE define
+        is defined for the CodeTools. To do this do this:
+
+  - go to Tools -> Codetools Defines Editor
+  - go to Edit -> Insert Node Below -> Define Recurse
+  - enter the following values:
+      Name: InLazIDE
+      Description: Define InLazIDE everywhere
+      Variable: InLazIDE
+      Value from text: 1
+}
+
 {$WARN 4055 off : Conversion between ordinals and pointers is not portable}
 interface
 
@@ -238,7 +252,7 @@ type
     function AsDouble : Double;
     function AsInteger: Integer;
     function AsError: HRESULT;
-    function AsChar: AnsiChar; inline;
+    function AsChar: Char; inline;
     function AsAnsiChar: AnsiChar;
     function AsWideChar: WideChar;
     function AsInt64: Int64;
@@ -643,8 +657,8 @@ type
   TRttiParameterArray = specialize TArray<TRttiParameter>;
 
   TMethodImplementationCallback = reference to procedure(aUserData: Pointer; const aArgs: TValueArray; out aResult: TValue);
-  TMethodImplementationCallbackMethod = procedure(aUserData: Pointer; const aArgs: TValueArray; out aResult: TValue) of object; deprecated 'Use TMethodImplementationCallback';
-  TMethodImplementationCallbackProc = procedure(aUserData: Pointer; const aArgs: TValueArray; out aResult: TValue); deprecated 'Use TMethodImplementationCallback';
+  TMethodImplementationCallbackMethod = procedure(aUserData: Pointer; const aArgs: TValueArray; out aResult: TValue) of object; {$ifndef InLazIDE}deprecated 'Use TMethodImplementationCallback';{$endif}
+  TMethodImplementationCallbackProc = procedure(aUserData: Pointer; const aArgs: TValueArray; out aResult: TValue); {$ifndef InLazIDE}deprecated 'Use TMethodImplementationCallback';{$endif}
   TFunctionCallParameterInfoArray = specialize TArray<TFunctionCallParameterInfo>;
   TPointerArray = specialize TArray<Pointer>;
 
@@ -677,8 +691,8 @@ type
     function GetFlags: TFunctionCallFlags; virtual; abstract;
   public type
     TCallback = reference to procedure(aInvokable: TRttiInvokableType; const aArgs: TValueArray; out aResult: TValue);
-    TCallbackMethod = procedure(aInvokable: TRttiInvokableType; const aArgs: TValueArray; out aResult: TValue) of object; deprecated 'Use TRttiInvokableType.TCallback';
-    TCallbackProc = procedure(aInvokable: TRttiInvokableType; const aArgs: TValueArray; out aResult: TValue); deprecated 'Use TRttiInvokableType.TCallback';
+    TCallbackMethod = procedure(aInvokable: TRttiInvokableType; const aArgs: TValueArray; out aResult: TValue) of object; {$ifndef InLazIDE}deprecated 'Use TRttiInvokableType.TCallback';{$endif}
+    TCallbackProc = procedure(aInvokable: TRttiInvokableType; const aArgs: TValueArray; out aResult: TValue); {$ifndef InLazIDE}deprecated 'Use TRttiInvokableType.TCallback';{$endif}
   public
     function GetParameters: TRttiParameterArray; inline;
     property CallingConvention: TCallConv read GetCallingConvention;
@@ -1082,7 +1096,8 @@ begin
     tkChar,
     tkWideChar,
     tkString,
-    tkLString:
+    tkLString,
+    tkAString:
       aType:=varString;
     tkUString:
       aType:=varUString;
@@ -3063,7 +3078,7 @@ begin
       TValue.Make(@Tmp,System.TypeInfo(WideString),aDest);
     tkUString:
       TValue.Make(@Tmp,System.TypeInfo(UnicodeString),aDest);
-    tkLString:
+    tkAString:
       begin
       SetString(S, PAnsiChar(@Tmp), 1);
       SetCodePage(S,GetTypeData(aDestType)^.CodePage);
@@ -3103,7 +3118,7 @@ begin
       US:=Tmp;
       TValue.Make(@US,System.TypeInfo(UnicodeString),aDest);
       end;
-    tkLString:
+    tkAString:
       begin
       SetString(RS,PAnsiChar(@Tmp),1);
       SetCodePage(RS,GetTypeData(aDestType)^.CodePage);
@@ -4643,9 +4658,9 @@ begin
     raise EInvalidCast.Create(SErrInvalidTypecast);
 end;
 
-function TValue.AsChar: AnsiChar;
+function TValue.AsChar: Char;
 begin
-{$if SizeOf(AnsiChar) = 1}
+{$if SizeOf(Char) = 1}
   Result := AsAnsiChar;
 {$else}
   Result := AsWideChar;

+ 11 - 5
packages/rtl-objpas/src/inc/variants.pp

@@ -2327,6 +2327,10 @@ begin
       Dest.vType := varOleStr;
       Dest.vOleStr := nil;
       WideString(Pointer(Dest.vOleStr)) := WideString(Pointer(vOleStr));
+    end else if vType = varUString then begin
+      Dest.vType := varUString;
+      Dest.vustring := Nil;
+      UnicodeString(Dest.vustring) := UnicodeString(vustring);
     end else if vType = varAny then begin
       Dest := Source;
       RefAnyProc(Dest);
@@ -2456,6 +2460,11 @@ begin
         varDate:     SysVarFromTDateTime(Variant(aDest), VariantToDate(aSource));
 {$endif}
         varOleStr:   DoVarCastWStr(aDest, aSource);
+        varUString:  begin
+          DoVarClearIfComplex(aDest);
+          aDest.vType := aVarType;
+          UnicodeString(aDest.vustring) := VariantToUnicodeString(aSource);
+        end;
         varBoolean:  SysVarFromBool(Variant(aDest), VariantToBoolean(aSource));
         varShortInt: SysVarFromInt(Variant(aDest), VariantToShortInt(aSource), -1);
         varByte:     SysVarFromInt(Variant(aDest), VariantToByte(aSource), 1);
@@ -2466,13 +2475,10 @@ begin
 
         varDispatch: DoVarCastDispatch(aDest, aSource);
         varUnknown:  DoVarCastInterface(aDest, aSource);
-      else
-        case aVarType of
-          varString: DoVarCastLStr(aDest, aSource);
-          varAny:    VarCastError(vType, varAny);
+        varString:   DoVarCastLStr(aDest, aSource);
+        varAny:      VarCastError(vType, varAny);
         else
           DoVarCastComplex(aDest, aSource, aVarType);
-        end;
       end;
     end;
 

+ 1 - 0
packages/rtl-objpas/src/inc/varutilh.inc

@@ -71,6 +71,7 @@ function VariantToInt64(const VargSrc : TVarData ) : Int64;
 function VariantToQWord(const VargSrc : TVarData ) : Qword;
 function VariantToWideString(const VargSrc : TVarData) : WideString;
 function VariantToAnsiString(const VargSrc : TVarData) : AnsiString;
+function VariantToUnicodeString(const VargSrc : TVarData) : UnicodeString;
 function VariantToShortString(const VargSrc : TVarData) : ShortString;
 
 {$ifdef USE_WINDOWS_OLE_FUNCTIONS}

+ 6 - 0
packages/rtl-objpas/src/inc/varutils.inc

@@ -77,6 +77,8 @@ begin
             ;
           varOleStr:
             WideString(Pointer(VOleStr)):='';
+          varUString:
+            UnicodeString(VUString):='';
           varDispatch,
           varUnknown:
             iinterface(vunknown):=nil;
@@ -115,6 +117,8 @@ begin
             Move(VBytes, VargDest.VBytes, SizeOf(VargDest.VBytes));
           varOleStr:
             CopyAsWideString(VargDest.VOleStr,VOleStr);
+          varUString:
+            UnicodeString(VargDest.vustring):=UnicodeString(vustring);
           varDispatch:
             IUnknown(VargDest.vdispatch):=IUnknown(VargSrc.vdispatch);
           varUnknown:
@@ -156,6 +160,7 @@ begin
       VarQWord    : VargDest.VQWord:=PQWord(VPointer)^;
       varVariant  : Variant(VargDest):=Variant(PVarData(VPointer)^);
       varOleStr   : CopyAsWideString(VargDest.VOleStr,PVarData(VPointer)^.VoleStr);
+      varUString  : UnicodeString(VargDest.vustring):=PUnicodeString(VPointer)^;
       varDispatch,
       varUnknown  : IInterface(VargDest.vUnknown):=IInterface(PInterface(VargSrc.VPointer)^);
       else
@@ -200,6 +205,7 @@ begin
           VarInt64    : VargDest.Vint64:=VariantToInt64(Tmp);
           VarLongWord : VargDest.VLongWord:=VariantToCardinal(Tmp);
           VarQWord    : VargDest.VQWord:=VariantToQword(tmp);
+          varUString  : UnicodeString(VargDest.vustring):=UnicodeString(tmp.vustring);
        else
           Result:=VAR_BADVARTYPE;
        end;

+ 61 - 0
packages/rtl-objpas/tests/tests.rtti.value.pas

@@ -130,6 +130,12 @@ Type
     Procedure TestFromVarRecQWord;
     Procedure TestFromVarRecUnicodeString;
     Procedure TestArrayOfConstToTValue;
+    procedure TestCastAnsiString;
+{$ifndef FPC_WIDESTRING_EQUAL_UNICODESTRING}
+    procedure TestCastUnicodeString;
+{$endif}
+    procedure TestCastWideString;
+    procedure TestCastShortString;
   end;
 
   { TMyUNknown }
@@ -719,6 +725,61 @@ begin
   CheckEquals(1.23,S[2].AsDouble,0.01,'Value 3');
 end;
 
+procedure TTestValueVariant.TestCastAnsiString;
+var
+  s: AnsiString;
+  v: Variant;
+  vvar, vstr: TValue;
+begin
+  s := 'Test';
+  v := s;
+  vvar := TValue.{$ifdef fpc}specialize{$endif}From<Variant>(v);
+  CheckTrue(vvar.TryCast(TypeInfo(AnsiString), vstr));
+  CheckEquals(s, vstr.AsAnsiString);
+end;
+
+{$ifndef FPC_WIDESTRING_EQUAL_UNICODESTRING}
+procedure TTestValueVariant.TestCastUnicodeString;
+var
+  u: UnicodeString;
+  v: Variant;
+  vvar, vstr: TValue;
+begin
+  u := 'Test';
+  TVarData(v).vType := varUString;
+  TVarData(v).vuString := Pointer(u);
+  vvar := TValue.{$ifdef fpc}specialize{$endif}From<Variant>(v);
+  CheckTrue(vvar.TryCast(TypeInfo(UnicodeString), vstr));
+  CheckEquals(u, vstr.AsUnicodeString);
+end;
+{$endif}
+
+procedure TTestValueVariant.TestCastWideString;
+var
+  w: WideString;
+  v: Variant;
+  vvar, vstr: TValue;
+begin
+  w := 'Test';
+  v := w;
+  vvar := TValue.{$ifdef fpc}specialize{$endif}From<Variant>(v);
+  CheckTrue(vvar.TryCast(TypeInfo(WideString), vstr));
+  CheckEquals(w, vstr.AsUnicodeString);
+end;
+
+procedure TTestValueVariant.TestCastShortString;
+var
+  s: ShortString;
+  v: Variant;
+  vvar, vstr: TValue;
+begin
+  s := 'Test';
+  v := s;
+  vvar := TValue.{$ifdef fpc}specialize{$endif}From<Variant>(v);
+  CheckTrue(vvar.TryCast(TypeInfo(ShortString), vstr));
+  CheckEquals(s, vstr.AsAnsiString);
+end;
+
 { TMyUNknown }
 
 function TMyUNknown.GetTypeInfoCount(out count: longint): HResult; stdcall;

+ 7 - 7
rtl/Makefile

@@ -2,7 +2,7 @@
 # Don't edit, this file is generated by FPCMake Version 2.0.0
 #
 default: all
-MAKEFILETARGETS=i386-linux i386-go32v2 i386-win32 i386-os2 i386-freebsd i386-beos i386-haiku i386-netbsd i386-solaris i386-qnx i386-netware i386-openbsd i386-wdosx i386-darwin i386-emx i386-watcom i386-netwlibc i386-wince i386-embedded i386-symbian i386-nativent i386-iphonesim i386-android i386-aros m68k-linux m68k-netbsd m68k-amiga m68k-atari m68k-palmos m68k-macosclassic m68k-embedded m68k-sinclairql m68k-human68k powerpc-linux powerpc-netbsd powerpc-amiga powerpc-macosclassic powerpc-darwin powerpc-morphos powerpc-embedded powerpc-wii powerpc-aix sparc-linux sparc-netbsd sparc-solaris sparc-embedded x86_64-linux x86_64-freebsd x86_64-haiku x86_64-netbsd x86_64-solaris x86_64-openbsd x86_64-darwin x86_64-win64 x86_64-embedded x86_64-iphonesim x86_64-android x86_64-aros x86_64-dragonfly arm-linux arm-netbsd arm-palmos arm-wince arm-gba arm-nds arm-embedded arm-symbian arm-android arm-aros arm-freertos arm-ios powerpc64-linux powerpc64-freebsd powerpc64-darwin powerpc64-embedded powerpc64-aix avr-embedded armeb-linux armeb-embedded mips-linux mipsel-linux mipsel-embedded mipsel-android mipsel-ps1 mips64-linux mips64el-linux jvm-java jvm-android i8086-embedded i8086-msdos i8086-win16 aarch64-linux aarch64-freebsd aarch64-darwin aarch64-win64 aarch64-embedded aarch64-iphonesim aarch64-android aarch64-ios wasm32-embedded wasm32-wasip1 wasm32-wasip1threads wasm32-wasip2 sparc64-linux riscv32-linux riscv32-embedded riscv32-freertos riscv64-linux riscv64-embedded xtensa-linux xtensa-embedded xtensa-freertos z80-embedded z80-zxspectrum z80-msxdos z80-amstradcpc loongarch64-linux
+MAKEFILETARGETS=i386-linux i386-go32v2 i386-win32 i386-os2 i386-freebsd i386-beos i386-haiku i386-netbsd i386-solaris i386-qnx i386-netware i386-openbsd i386-wdosx i386-darwin i386-emx i386-watcom i386-netwlibc i386-wince i386-embedded i386-symbian i386-nativent i386-iphonesim i386-android i386-aros m68k-linux m68k-netbsd m68k-amiga m68k-atari m68k-palmos m68k-macosclassic m68k-embedded m68k-sinclairql m68k-human68k powerpc-linux powerpc-netbsd powerpc-amiga powerpc-macosclassic powerpc-darwin powerpc-morphos powerpc-embedded powerpc-wii powerpc-aix sparc-linux sparc-netbsd sparc-solaris sparc-embedded x86_64-linux x86_64-freebsd x86_64-haiku x86_64-netbsd x86_64-solaris x86_64-openbsd x86_64-darwin x86_64-win64 x86_64-embedded x86_64-iphonesim x86_64-android x86_64-aros x86_64-dragonfly arm-linux arm-netbsd arm-palmos arm-wince arm-gba arm-nds arm-embedded arm-symbian arm-android arm-aros arm-freertos arm-ios powerpc64-linux powerpc64-darwin powerpc64-embedded powerpc64-aix powerpc64-freebsd avr-embedded armeb-linux armeb-embedded mips-linux mipsel-linux mipsel-embedded mipsel-android mipsel-ps1 mips64-linux mips64el-linux jvm-java jvm-android i8086-embedded i8086-msdos i8086-win16 aarch64-linux aarch64-freebsd aarch64-darwin aarch64-win64 aarch64-embedded aarch64-iphonesim aarch64-android aarch64-ios wasm32-embedded wasm32-wasip1 wasm32-wasip1threads wasm32-wasip2 sparc64-linux riscv32-linux riscv32-embedded riscv32-freertos riscv64-linux riscv64-embedded xtensa-linux xtensa-embedded xtensa-freertos z80-embedded z80-zxspectrum z80-msxdos z80-amstradcpc loongarch64-linux
 BSDs = freebsd netbsd openbsd darwin dragonfly
 UNIXs = linux $(BSDs) solaris qnx haiku aix
 LIMIT83fs = go32v2 os2 emx watcom msdos win16 atari human68k
@@ -581,9 +581,6 @@ endif
 ifeq ($(CPU_OS_TARGET),powerpc64-linux)
 override TARGET_DIRS+=linux
 endif
-ifeq ($(CPU_OS_TARGET),powerpc64-freebsd)
-override TARGET_DIRS+=freebsd
-endif
 ifeq ($(CPU_OS_TARGET),powerpc64-darwin)
 override TARGET_DIRS+=darwin
 endif
@@ -593,6 +590,9 @@ endif
 ifeq ($(CPU_OS_TARGET),powerpc64-aix)
 override TARGET_DIRS+=aix
 endif
+ifeq ($(CPU_OS_TARGET),powerpc64-freebsd)
+override TARGET_DIRS+=freebsd
+endif
 ifeq ($(CPU_OS_TARGET),avr-embedded)
 override TARGET_DIRS+=embedded
 endif
@@ -2276,9 +2276,6 @@ endif
 ifeq ($(CPU_OS_TARGET),powerpc64-linux)
 TARGET_DIRS_LINUX=1
 endif
-ifeq ($(CPU_OS_TARGET),powerpc64-freebsd)
-TARGET_DIRS_FREEBSD=1
-endif
 ifeq ($(CPU_OS_TARGET),powerpc64-darwin)
 TARGET_DIRS_DARWIN=1
 endif
@@ -2288,6 +2285,9 @@ endif
 ifeq ($(CPU_OS_TARGET),powerpc64-aix)
 TARGET_DIRS_AIX=1
 endif
+ifeq ($(CPU_OS_TARGET),powerpc64-freebsd)
+TARGET_DIRS_FREEBSD=1
+endif
 ifeq ($(CPU_OS_TARGET),avr-embedded)
 TARGET_DIRS_EMBEDDED=1
 endif

+ 2 - 5
rtl/bsd/system.pp

@@ -208,11 +208,8 @@ begin
   e[j]:=1 shl i;
   { this routine is called from a signal handler, so must not change errno }
   olderrno:=geterrno;
-  seterrno(0);
-  if fpsigprocmask(SIG_UNBLOCK,@e,@oe)<>0 then
-    reenable_signal:=geterrno=0
-  else
-    reenable_signal:=true;
+  fpsigprocmask(SIG_UNBLOCK,@e,@oe);
+  reenable_signal:=geterrno=0;
   seterrno(olderrno);
 end;
 

+ 22 - 1
rtl/freebsd/Makefile

@@ -2,7 +2,7 @@
 # Don't edit, this file is generated by FPCMake Version 2.0.0
 #
 default: all
-MAKEFILETARGETS=i386-freebsd x86_64-freebsd aarch64-freebsd
+MAKEFILETARGETS=i386-freebsd x86_64-freebsd aarch64-freebsd powerpc64-freebsd
 BSDs = freebsd netbsd openbsd darwin dragonfly
 UNIXs = linux $(BSDs) solaris qnx haiku aix
 LIMIT83fs = go32v2 os2 emx watcom msdos win16 atari human68k
@@ -943,6 +943,9 @@ ifeq ($(ARCH),x86_64)
 CPU_UNITS=$(X86UNIT) $(PORTSUNIT) $(CPUUNIT) $(INTRINSICSUNIT)
 ASTARGET=--64
 endif
+ifeq ($(ARCH),powerpc64)
+ASTARGET=-a64
+endif
 ifeq ($(ARCH),aarch64)
 CPU_UNITS=$(INTRINSICSUNIT)
 endif
@@ -956,6 +959,9 @@ endif
 ifeq ($(CPU_OS_TARGET),aarch64-freebsd)
 override TARGET_UNITS+=$(SYSTEMUNIT) $(UUCHARUNIT) $(UNIXTYPEUNIT) $(CTYPESUNIT) $(OBJPASUNIT) $(MACPASUNIT) $(ISO7185UNIT) $(EXTPASUNIT) $(STRINGSUNIT) $(SYSCALLUNIT) $(SYSCTLUNIT) $(BASEUNIXUNIT) $(UNIXUTILUNIT) $(CHARACTERUNIT) $(UNIXUNIT) $(RTLCONSTSUNIT) $(INITCUNIT) $(CMEMUNIT) $(DLUNIT) $(TERMIOUNIT) $(SYSUTILSUNIT) $(MATHUNIT) $(TYPINFOUNIT) $(TYPESUNIT) $(SORTBASEUNIT) $(CLASSESUNIT) $(FGLUNIT) $(DYNLIBSUNIT) $(CPU_UNITS) $(CHARSETUNIT) $(CPALLUNIT) $(GETOPTSUNIT) $(HEAPTRCUNIT) $(LNFODWRFUNIT) $(LINEINFOUNIT) $(ERRORSUNIT) $(BSDUNIT) $(FREEBSDUNIT) $(CONSOLEUNIT) $(SYSCONSTUNIT) $(CTHREADSUNIT) $(DOSUNIT) $(CWSTRINGUNIT) $(FPINTRESUNIT) $(UNIXCPUNIT) $(FPWIDESTRINGUNIT) $(SOFTFPUUNIT) $(SFPUX80UNIT) $(UFLOATX80UNIT) $(SFPU128UNIT) $(UFLOAT128UNIT) $(UNIX98PTYUNIT)
 endif
+ifeq ($(CPU_OS_TARGET),powerpc64-freebsd)
+override TARGET_UNITS+=$(SYSTEMUNIT) $(UUCHARUNIT) $(UNIXTYPEUNIT) $(CTYPESUNIT) $(OBJPASUNIT) $(MACPASUNIT) $(ISO7185UNIT) $(EXTPASUNIT) $(STRINGSUNIT) $(SYSCALLUNIT) $(SYSCTLUNIT) $(BASEUNIXUNIT) $(UNIXUTILUNIT) $(CHARACTERUNIT) $(UNIXUNIT) $(RTLCONSTSUNIT) $(INITCUNIT) $(CMEMUNIT) $(DLUNIT) $(TERMIOUNIT) $(SYSUTILSUNIT) $(MATHUNIT) $(TYPINFOUNIT) $(TYPESUNIT) $(SORTBASEUNIT) $(CLASSESUNIT) $(FGLUNIT) $(DYNLIBSUNIT) $(CPU_UNITS) $(CHARSETUNIT) $(CPALLUNIT) $(GETOPTSUNIT) $(HEAPTRCUNIT) $(LNFODWRFUNIT) $(LINEINFOUNIT) $(ERRORSUNIT) $(BSDUNIT) $(FREEBSDUNIT) $(CONSOLEUNIT) $(SYSCONSTUNIT) $(CTHREADSUNIT) $(DOSUNIT) $(CWSTRINGUNIT) $(FPINTRESUNIT) $(UNIXCPUNIT) $(FPWIDESTRINGUNIT) $(SOFTFPUUNIT) $(SFPUX80UNIT) $(UFLOATX80UNIT) $(SFPU128UNIT) $(UFLOAT128UNIT) $(UNIX98PTYUNIT)
+endif
 ifeq ($(CPU_OS_TARGET),i386-freebsd)
 override TARGET_IMPLICITUNITS+=$(EXEINFOUNIT) $(CP_UNITS) $(UNICODEDATAUNIT)
 endif
@@ -965,6 +971,9 @@ endif
 ifeq ($(CPU_OS_TARGET),aarch64-freebsd)
 override TARGET_IMPLICITUNITS+=$(EXEINFOUNIT) $(CP_UNITS) $(UNICODEDATAUNIT)
 endif
+ifeq ($(CPU_OS_TARGET),powerpc64-freebsd)
+override TARGET_IMPLICITUNITS+=$(EXEINFOUNIT) $(CP_UNITS) $(UNICODEDATAUNIT)
+endif
 ifeq ($(CPU_OS_TARGET),i386-freebsd)
 override TARGET_LOADERS+=prt0 cprt0 gprt0 dllprt0
 endif
@@ -974,6 +983,9 @@ endif
 ifeq ($(CPU_OS_TARGET),aarch64-freebsd)
 override TARGET_LOADERS+=prt0 cprt0 gprt0 dllprt0
 endif
+ifeq ($(CPU_OS_TARGET),powerpc64-freebsd)
+override TARGET_LOADERS+=prt0 cprt0 gprt0 dllprt0
+endif
 ifeq ($(CPU_OS_TARGET),i386-freebsd)
 override TARGET_RSTS+=$(MATHUNIT) $(TYPINFOUNIT) $(CLASSESUNIT) $(SYSCONSTUNIT) $(UNICODEDATAUNIT)
 endif
@@ -983,6 +995,9 @@ endif
 ifeq ($(CPU_OS_TARGET),aarch64-freebsd)
 override TARGET_RSTS+=$(MATHUNIT) $(TYPINFOUNIT) $(CLASSESUNIT) $(SYSCONSTUNIT) $(UNICODEDATAUNIT)
 endif
+ifeq ($(CPU_OS_TARGET),powerpc64-freebsd)
+override TARGET_RSTS+=$(MATHUNIT) $(TYPINFOUNIT) $(CLASSESUNIT) $(SYSCONSTUNIT) $(UNICODEDATAUNIT)
+endif
 override INSTALL_FPCPACKAGE=y y y
 ifeq ($(CPU_OS_TARGET),i386-freebsd)
 override COMPILER_INCLUDEDIR+=$(INC) $(PROCINC) $(UNIXINC) $(BSDINC) $(BSDPROCINC) $(OSPROCINC)
@@ -993,6 +1008,9 @@ endif
 ifeq ($(CPU_OS_TARGET),aarch64-freebsd)
 override COMPILER_INCLUDEDIR+=$(INC) $(PROCINC) $(UNIXINC) $(BSDINC) $(BSDPROCINC) $(OSPROCINC)
 endif
+ifeq ($(CPU_OS_TARGET),powerpc64-freebsd)
+override COMPILER_INCLUDEDIR+=$(INC) $(PROCINC) $(UNIXINC) $(BSDINC) $(BSDPROCINC) $(OSPROCINC)
+endif
 ifeq ($(CPU_OS_TARGET),i386-freebsd)
 override COMPILER_SOURCEDIR+=$(INC) $(PROCINC) $(UNIXINC) $(BSDINC) $(COMMON)
 endif
@@ -1002,6 +1020,9 @@ endif
 ifeq ($(CPU_OS_TARGET),aarch64-freebsd)
 override COMPILER_SOURCEDIR+=$(INC) $(PROCINC) $(UNIXINC) $(BSDINC) $(COMMON)
 endif
+ifeq ($(CPU_OS_TARGET),powerpc64-freebsd)
+override COMPILER_SOURCEDIR+=$(INC) $(PROCINC) $(UNIXINC) $(BSDINC) $(COMMON)
+endif
 ifdef REQUIRE_UNITSDIR
 override UNITSDIR+=$(REQUIRE_UNITSDIR)
 endif

+ 0 - 5
rtl/freebsd/Makefile.fpc

@@ -84,15 +84,10 @@ ifeq ($(ARCH),x86_64)
 CPU_UNITS=$(X86UNIT) $(PORTSUNIT) $(CPUUNIT) $(INTRINSICSUNIT)
 ASTARGET=--64
 endif
-
 ifeq ($(ARCH),aarch64)
 CPU_UNITS=$(INTRINSICSUNIT)
 endif
 
-ifeq ($(ARCH),powerpc64)
-CPU_UNITS=$(INTRINSICSUNIT)
-endif
-
 # Paths
 OBJPASDIR=$(RTL)/objpas
 

+ 2 - 1
rtl/freebsd/powerpc64/cprt0.as

@@ -6,7 +6,8 @@
         /* FreeBSD/ppc64: avoid @toc@ha/@toc@l relocations (not supported by some assemblers).
            Use full 64-bit absolute address materialization for data symbols. */
         .macro  LOAD_64BIT_ADDR ra, sym
-		ld	\ra,(\sym)@got(2)
+		addis     \ra,2,\sym@toc@ha
+		ld        \ra,\sym@toc@l(\ra)
         .endm
 
         .section .rodata

+ 1 - 0
rtl/inc/generic.inc

@@ -14,6 +14,7 @@
 
  **********************************************************************}
 
+
 function align(addr : PtrUInt;alignment : PtrUInt) : PtrUInt;{$ifdef SYSTEMINLINE}inline;{$endif}
   var
     tmp,am1 : PtrUInt;

+ 0 - 3
rtl/unix/classes.pp

@@ -41,9 +41,6 @@ unit Classes;
 
 interface
 
-{$ifdef NO_FPC_USE_INTRINSICS}
-  {$undef FPC_USE_INTRINSICS}
-{$endif}
 {$IFDEF FPC_DOTTEDUNITS}
 uses
   System.SysUtils,

+ 1 - 1
utils/fpcm/fpcmmain.pp

@@ -172,7 +172,7 @@ interface
         { amiga }   ( false, false, true,  true,  false, false, false, false, false, false, false, false, false, false, false,   false, false, false,  false, false, false,  false,  false, false, false),
         { atari }   ( false, false, true,  false, false, false, false, false, false, false, false, false, false, false, false,   false, false, false,  false, false, false,  false,  false, false, false),
         { solaris } ( false, true,  false, false, true,  true,  false, false, false, false, false, false, false, false, false,   false, false, false,  false, false, false,  false,  false, false, false),
-        { qnx }     ( false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,   false, false, false,  false, false, false,  false,  false, false, false),
+        { qnx }     ( false, true,  false, false, false, false, false, false, false, false, false, false, false, false, false,   false, false, false,  false, false, false,  false,  false, false, false),
         { netware } ( false, true,  false, false, false, false, false, false, false, false, false, false, false, false, false,   false, false, false,  false, false, false,  false,  false, false, false),
         { openbsd } ( false, true,  false, false, false, true,  false, false, false, false, false, false, false, false, false,   false, false, false,  false, false, false,  false,  false, false, false),
         { wdosx }   ( false, true,  false, false, false, false, false, false, false, false, false, false, false, false, false,   false, false, false,  false, false, false,  false,  false, false, false),