This solution contains a reusable converter for importing Microsoft SSRS RDL report definitions and generating editable TX Text Control templates.
The converter is intentionally not an SSRS renderer. It reads the report structure, extracts the template intent, and creates a DOCX or TX Text Control template that can be edited and used with TX Text Control MailMerge.
TextControl.RdlImport: reusable conversion engine.TextControl.RdlImport.Tests: parser, mapper, generator, and integration tests.TextControl.RdlImport.Tests/TestData/Rdl: sample RDL fixtures.TextControl.RdlImport.Tests/TestOutput: generated DOCX, TX, and JSON files from test runs.
The library is split into small layers:
Parsing: reads RDL XML into a neutral RDL model.Model: contains RDL concepts such as reports, textboxes, tablix regions, groups, page settings, images, subreports, and styles.Analysis: creates a JSON-friendly conversion report with supported features, warnings, unsupported items, and a quality score.Mapping: maps the RDL model to a neutral template document model.Generation: writes the template model with TX Text ControlServerTextControl.
TX-specific code is isolated in TxTemplateGenerator. It sets:
ServerTextControl.EntryAssembly = typeof(TxTemplateGenerator).Assembly;This keeps licensing/package ownership in the reusable library assembly.
Current supported RDL features include:
- root-level
Body/Page - RDL 2016
ReportSections - page size and margins
- page headers and footers
- textboxes
- positioned textboxes as paragraphs with indentation
- same-row positioned textboxes as layout tables
- embedded images
- inline and fixed-position images
- tablix tables
- nested tablix tables
- tablix row hierarchy parsing
- detail/group-aware merge block placement
- TX merge blocks as
SubTextParts namedtxmb_<DataSetName> - nested merge blocks when the parent block is created before nested block insertion
ApplicationFieldmerge fields with visible text such as«InvoiceNo»INCLUDETEXTfields for subreports- subreport parameter mappings as adjacent merge fields
- RDL report variables for literal style values, for example color variables
- table column widths
- table row heights
- cell background colors
- cell borders
- cell padding
- vertical alignment
- paragraph alignment
- font family
- font size
- text color
- bold
- italic
- underline
- strikeout
- static text, field expressions, parameter expressions, and simple concatenation
Unsupported or partial areas are reported in the conversion JSON. Subreports are converted as INCLUDETEXT fields; referenced child RDL files are not expanded inline.
The converter supports file, stream, and in-memory workflows.
var converter = new RdlToTxConverter();
RdlConversionResult result = converter.Convert(new RdlConversionOptions
{
InputFile = @"C:\Reports\invoice.rdl",
OutputFile = @"C:\Reports\invoice.docx",
ReportFile = @"C:\Reports\invoice.json",
OutputFormat = TxTemplateFormat.Docx
});To save native TX Text Control format:
converter.Convert(new RdlConversionOptions
{
InputFile = @"C:\Reports\invoice.rdl",
OutputFile = @"C:\Reports\invoice.tx",
OutputFormat = TxTemplateFormat.InternalUnicode
});Use this for web uploads, database blobs, cloud storage, or any pipeline that should avoid file paths.
using Stream rdlInput = GetUploadedRdlStream();
using MemoryStream txOutput = new();
using MemoryStream reportJson = new();
var converter = new RdlToTxConverter();
RdlConversionResult result = converter.Convert(new RdlStreamConversionOptions
{
InputStream = rdlInput,
OutputStream = txOutput,
ReportStream = reportJson,
ReportName = "invoice",
OutputFormat = TxTemplateFormat.InternalUnicode
});
byte[] txBytes = txOutput.ToArray();
byte[] reportBytes = reportJson.ToArray();By default, stream conversion leaves caller-provided streams open. Set LeaveOpen = false if the converter should dispose them.
Use this when the RDL is already loaded as bytes and the generated template should also be returned as bytes.
byte[] rdlBytes = File.ReadAllBytes(@"C:\Reports\invoice.rdl");
var converter = new RdlToTxConverter();
RdlMemoryConversionResult result = converter.ConvertToMemory(
rdlBytes,
TxTemplateFormat.Docx,
"invoice");
byte[] docxBytes = result.Document;
byte[] reportJsonBytes = result.ReportJson;
ConversionReport report = result.Conversion.ConversionReport;For document bytes only:
byte[] txBytes = converter.ConvertDocumentToMemory(
rdlBytes,
TxTemplateFormat.InternalUnicode,
"invoice");RdlConversionResult analysis = converter.Analyze(@"C:\Reports\invoice.rdl");
Console.WriteLine(analysis.ConversionReport.QualityScore);TxTemplateFormat.Docx
- Saved through TX Text Control as WordprocessingML/DOCX.
- Useful for inspection, Word editing, and compatibility checks.
TxTemplateFormat.InternalUnicode
- Native TX Text Control format.
- Preserves TX structures such as
SubTextPartmerge blocks. - Recommended for runtime TX Text Control template workflows.
The generator uses the TX byte-array save API:
tx.Save(out byte[] documentBytes, BinaryStreamType.WordprocessingML);
tx.Save(out byte[] documentBytes, BinaryStreamType.InternalUnicodeFormat);The test project contains realistic fixtures that exercise different features:
invoice-basic.rdl: basic invoice with line-item merge block.inventory-tabular.rdl: simple inventory table.letter-parameters.rdl: parameter fields.sales-order-alignment.rdl: paragraph and numeric alignment.positioned-layout.rdl: positioned paragraphs.multi-textbox-row.rdl: same-row positioned textboxes as layout tables.embedded-image-logo.rdl: fixed-position embedded image.embedded-image-inline.rdl: inline embedded image.header-footer-basic.rdl: headers, footers, and fields in header/footer parts.styled-table-cells.rdl: table cell colors, borders, padding, row heights, text color, and inline image inside a cell.nested-tables.rdl: nested tablix and nested merge blocks.report-sections-subreports.rdl: RDL 2016 sections, variables, subreports asINCLUDETEXT.complex-invoice-supported-features.rdl: broad feature sample with header/footer, images, nested merge blocks, cell styling,INCLUDETEXT, and typography.grouped-tablix-detail-row.rdl:TablixRowHierarchydetail-row detection.text-decorations.rdl: bold, italic, underline, and strikeout.
dotnet test tx-rdl2tx.slnxThe integration test converts every fixture to:
.docx.tx.json
Generated files are written to:
TextControl.RdlImport.Tests/TestOutput
The tests also verify:
- stream-to-stream conversion
- byte-array conversion
- generated DOCX package contents
- native TX files can be loaded by
ServerTextControl - expected merge blocks exist
- header/footer fields are placed in the correct DOCX parts
- supported formatting is serialized into the output
Each conversion can produce a JSON report:
{
"ReportName": "complex-invoice-supported-features",
"QualityScore": 95,
"TextboxCount": 28,
"TablixCount": 2,
"ImageCount": 1,
"LineCount": 0,
"Supported": [
"Textboxes: 28",
"Simple tablix regions: 2",
"Embedded images: 1",
"Subreport INCLUDETEXT fields: 1",
"Page header content",
"Page footer content"
],
"Warnings": [
"Subreports: 1 INCLUDETEXT field(s) generated. Parameter mappings are emitted as adjacent merge fields."
],
"Unsupported": []
}The score is a practical confidence indicator, not a proof of visual equivalence.
Repeated tablix rows are converted to TX Text Control SubTextParts. The subtextpart name is:
txmb_<DataSetName>
For example:
txmb_InvoiceItems
The converter now uses TablixRowHierarchy when available to choose the correct detail/group row. If no useful hierarchy is present, it falls back to the first row containing merge fields.
For nested merge blocks, the parent merge block is created first, then nested content is inserted so TX Text Control accepts both subtextparts.
SSRS subreports normally reference separate reports. They are not embedded in the parent RDL. The converter maps them to INCLUDETEXT fields:
INCLUDETEXT "Report Documentation Variables"
Parameter mappings are emitted next to the include as regular text and merge fields, for example:
(ItemID: «ItemID»)
Resolving the include target to a converted .tx or .docx file is intentionally left to the consuming application or a later include-path strategy.
The converter does not currently implement:
- full SSRS expression evaluation
- conditional visibility
- conditional formatting beyond simple literal variable references
- charts, maps, gauges, and report viewer specific items
- full subreport expansion from referenced RDL files
- drillthrough/hyperlink action conversion
- exact SSRS rendering behavior
The design goal is a maintainable TX Text Control template, not pixel-perfect SSRS output.