or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

caching-loading.mdcore-processing.mdexception-handling.mdextensions.mdindex.mdobject-wrapping.mdoutput-formats.mdtemplate-models.md

output-formats.mddocs/

0

# Output Formats and Escaping

1

2

FreeMarker's output format system provides automatic escaping and content-type-aware processing to prevent security vulnerabilities like XSS attacks while ensuring proper content formatting.

3

4

## Core Output Format Classes

5

6

### Base Output Format

7

8

```java { .api }

9

abstract class OutputFormat {

10

// Format identification

11

abstract String getName();

12

abstract String getMimeType();

13

abstract boolean isOutputFormatMixingAllowed(OutputFormat otherOutputFormat);

14

15

// Escaping capabilities

16

abstract boolean isAutoEscapedByDefault();

17

18

// Format compatibility

19

boolean isMarkupFormat();

20

boolean isSourceCodeFormat();

21

}

22

```

23

24

### Markup Output Format Base

25

26

```java { .api }

27

abstract class MarkupOutputFormat<MO extends TemplateMarkupOutputModel<MO>> extends OutputFormat {

28

// Markup-specific methods

29

abstract MO fromPlainTextByEscaping(String textToEsc) throws TemplateModelException;

30

abstract MO fromMarkup(String markupText) throws TemplateModelException;

31

abstract MO getEmpty();

32

33

// Markup concatenation

34

abstract MO concat(MO... outputModels) throws TemplateModelException;

35

36

// Format information

37

boolean isAutoEscapedByDefault();

38

boolean isMarkupFormat();

39

}

40

41

// Common markup format base

42

abstract class CommonMarkupOutputFormat<MO extends CommonTemplateMarkupOutputModel<MO>>

43

extends MarkupOutputFormat<MO> {

44

45

// Common escaping functionality

46

abstract String escapePlainText(String plainTextContent);

47

abstract boolean isLegacyBuiltInBypassed(String builtInName);

48

}

49

```

50

51

## Built-in Output Formats

52

53

### HTML Output Format

54

55

```java { .api }

56

class HTMLOutputFormat extends CommonMarkupOutputFormat<TemplateHTMLOutputModel> {

57

// Singleton instance

58

static final HTMLOutputFormat INSTANCE = new HTMLOutputFormat();

59

60

// Format identification

61

String getName();

62

String getMimeType();

63

64

// HTML-specific escaping

65

String escapePlainText(String plainTextContent);

66

TemplateHTMLOutputModel fromPlainTextByEscaping(String textToEsc) throws TemplateModelException;

67

TemplateHTMLOutputModel fromMarkup(String markupText) throws TemplateModelException;

68

TemplateHTMLOutputModel getEmpty();

69

70

// HTML model creation

71

TemplateHTMLOutputModel fromPlainTextByEscaping(String textToEsc);

72

TemplateHTMLOutputModel fromMarkup(String markupText);

73

}

74

75

// HTML output model

76

interface TemplateHTMLOutputModel extends CommonTemplateMarkupOutputModel<TemplateHTMLOutputModel> {

77

String getMarkupString() throws TemplateModelException;

78

}

79

```

80

81

### XML Output Format

82

83

```java { .api }

84

class XMLOutputFormat extends CommonMarkupOutputFormat<TemplateXMLOutputModel> {

85

// Singleton instance

86

static final XMLOutputFormat INSTANCE = new XMLOutputFormat();

87

88

// Format identification

89

String getName();

90

String getMimeType();

91

92

// XML-specific escaping

93

String escapePlainText(String plainTextContent);

94

TemplateXMLOutputModel fromPlainTextByEscaping(String textToEsc) throws TemplateModelException;

95

TemplateXMLOutputModel fromMarkup(String markupText) throws TemplateModelException;

96

TemplateXMLOutputModel getEmpty();

97

}

98

99

// XML output model

100

interface TemplateXMLOutputModel extends CommonTemplateMarkupOutputModel<TemplateXMLOutputModel> {

101

String getMarkupString() throws TemplateModelException;

102

}

103

```

104

105

### XHTML Output Format

106

107

```java { .api }

108

class XHTMLOutputFormat extends HTMLOutputFormat {

109

// Singleton instance

110

static final XHTMLOutputFormat INSTANCE = new XHTMLOutputFormat();

111

112

// Format identification (inherits most from HTMLOutputFormat)

113

String getName();

114

String getMimeType();

115

}

116

```

117

118

### RTF Output Format

119

120

```java { .api }

121

class RTFOutputFormat extends CommonMarkupOutputFormat<TemplateRTFOutputModel> {

122

// Singleton instance

123

static final RTFOutputFormat INSTANCE = new RTFOutputFormat();

124

125

// Format identification

126

String getName();

127

String getMimeType();

128

129

// RTF-specific escaping

130

String escapePlainText(String plainTextContent);

131

TemplateRTFOutputModel fromPlainTextByEscaping(String textToEsc) throws TemplateModelException;

132

TemplateRTFOutputModel fromMarkup(String markupText) throws TemplateModelException;

133

TemplateRTFOutputModel getEmpty();

134

}

135

```

136

137

### Plain Text Output Format

138

139

```java { .api }

140

class PlainTextOutputFormat extends OutputFormat {

141

// Singleton instance

142

static final PlainTextOutputFormat INSTANCE = new PlainTextOutputFormat();

143

144

// Format identification

145

String getName();

146

String getMimeType();

147

boolean isAutoEscapedByDefault();

148

boolean isOutputFormatMixingAllowed(OutputFormat otherOutputFormat);

149

}

150

```

151

152

### Undefined Output Format

153

154

```java { .api }

155

class UndefinedOutputFormat extends OutputFormat {

156

// Singleton instance

157

static final UndefinedOutputFormat INSTANCE = new UndefinedOutputFormat();

158

159

// Format identification

160

String getName();

161

String getMimeType();

162

boolean isAutoEscapedByDefault();

163

boolean isOutputFormatMixingAllowed(OutputFormat otherOutputFormat);

164

}

165

```

166

167

## Configuration and Usage

168

169

### Output Format Configuration

170

171

```java

172

Configuration cfg = new Configuration(Configuration.VERSION_2_3_34);

173

174

// Set default output format

175

cfg.setOutputFormat(HTMLOutputFormat.INSTANCE);

176

177

// Configure auto-escaping policy

178

cfg.setAutoEscapingPolicy(Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY);

179

180

// Register custom output formats

181

Set<OutputFormat> customFormats = new HashSet<>();

182

customFormats.add(new CustomJSONOutputFormat());

183

cfg.setRegisteredCustomOutputFormats(customFormats);

184

185

// Recognize standard file extensions for automatic format selection

186

cfg.setRecognizeStandardFileExtensions(true);

187

```

188

189

### Template-Specific Output Format

190

191

```java

192

// Configure specific output format for certain templates

193

TemplateConfiguration htmlConfig = new TemplateConfiguration();

194

htmlConfig.setOutputFormat(HTMLOutputFormat.INSTANCE);

195

htmlConfig.setAutoEscapingPolicy(Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY);

196

197

cfg.setTemplateConfigurations(

198

new ConditionalTemplateConfigurationFactory(

199

new FileExtensionMatcher("html"),

200

new FirstMatchTemplateConfigurationFactory(htmlConfig)

201

)

202

);

203

204

// Plain text for email templates

205

TemplateConfiguration textConfig = new TemplateConfiguration();

206

textConfig.setOutputFormat(PlainTextOutputFormat.INSTANCE);

207

208

cfg.setTemplateConfigurations(

209

new ConditionalTemplateConfigurationFactory(

210

new PathGlobMatcher("email/**"),

211

new FirstMatchTemplateConfigurationFactory(textConfig)

212

)

213

);

214

```

215

216

## Auto-Escaping Policies

217

218

```java { .api }

219

// Auto-escaping policy constants in Configuration

220

static final int ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY = 0;

221

static final int ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY = 1;

222

static final int DISABLE_AUTO_ESCAPING_POLICY = 2;

223

```

224

225

### Auto-Escaping Configuration Examples

226

227

```java

228

Configuration cfg = new Configuration(Configuration.VERSION_2_3_34);

229

230

// Enable auto-escaping only for formats where it's default (like HTML)

231

cfg.setAutoEscapingPolicy(Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY);

232

233

// Enable auto-escaping for all markup formats that support it

234

cfg.setAutoEscapingPolicy(Configuration.ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY);

235

236

// Disable auto-escaping completely (not recommended for security)

237

cfg.setAutoEscapingPolicy(Configuration.DISABLE_AUTO_ESCAPING_POLICY);

238

```

239

240

## Template Markup Output Models

241

242

### Common Template Markup Output Model

243

244

```java { .api }

245

interface CommonTemplateMarkupOutputModel<MO extends CommonTemplateMarkupOutputModel<MO>>

246

extends TemplateMarkupOutputModel<MO> {

247

248

String getMarkupString() throws TemplateModelException;

249

MO concat(MO... outputModels) throws TemplateModelException;

250

}

251

252

// Base markup output model

253

interface TemplateMarkupOutputModel<MO extends TemplateMarkupOutputModel<MO>>

254

extends TemplateModel {

255

256

MO getOutputFormat();

257

}

258

```

259

260

### Specific Output Models

261

262

```java { .api }

263

// HTML output model implementation

264

class TemplateHTMLOutputModelImpl implements TemplateHTMLOutputModel {

265

TemplateHTMLOutputModelImpl(String markupContent, HTMLOutputFormat outputFormat);

266

String getMarkupString() throws TemplateModelException;

267

TemplateHTMLOutputModel concat(TemplateHTMLOutputModel... outputModels) throws TemplateModelException;

268

HTMLOutputFormat getOutputFormat();

269

}

270

271

// XML output model implementation

272

class TemplateXMLOutputModelImpl implements TemplateXMLOutputModel {

273

TemplateXMLOutputModelImpl(String markupContent, XMLOutputFormat outputFormat);

274

String getMarkupString() throws TemplateModelException;

275

TemplateXMLOutputModel concat(TemplateXMLOutputModel... outputModels) throws TemplateModelException;

276

XMLOutputFormat getOutputFormat();

277

}

278

```

279

280

## Custom Output Formats

281

282

### Creating Custom Output Formats

283

284

```java

285

public class JSONOutputFormat extends OutputFormat {

286

public static final JSONOutputFormat INSTANCE = new JSONOutputFormat();

287

288

private JSONOutputFormat() {

289

// Private constructor for singleton

290

}

291

292

@Override

293

public String getName() {

294

return "JSON";

295

}

296

297

@Override

298

public String getMimeType() {

299

return "application/json";

300

}

301

302

@Override

303

public boolean isAutoEscapedByDefault() {

304

return true;

305

}

306

307

@Override

308

public boolean isOutputFormatMixingAllowed(OutputFormat otherOutputFormat) {

309

return otherOutputFormat instanceof JSONOutputFormat ||

310

otherOutputFormat instanceof PlainTextOutputFormat;

311

}

312

}

313

314

// JSON markup output format with escaping

315

public class JSONMarkupOutputFormat extends MarkupOutputFormat<TemplateJSONOutputModel> {

316

public static final JSONMarkupOutputFormat INSTANCE = new JSONMarkupOutputFormat();

317

318

@Override

319

public TemplateJSONOutputModel fromPlainTextByEscaping(String textToEsc)

320

throws TemplateModelException {

321

return new TemplateJSONOutputModelImpl(escapeJSON(textToEsc), this);

322

}

323

324

@Override

325

public TemplateJSONOutputModel fromMarkup(String markupText)

326

throws TemplateModelException {

327

return new TemplateJSONOutputModelImpl(markupText, this);

328

}

329

330

@Override

331

public TemplateJSONOutputModel getEmpty() {

332

return new TemplateJSONOutputModelImpl("", this);

333

}

334

335

@Override

336

public TemplateJSONOutputModel concat(TemplateJSONOutputModel... outputModels)

337

throws TemplateModelException {

338

StringBuilder sb = new StringBuilder();

339

for (TemplateJSONOutputModel model : outputModels) {

340

sb.append(model.getMarkupString());

341

}

342

return new TemplateJSONOutputModelImpl(sb.toString(), this);

343

}

344

345

private String escapeJSON(String text) {

346

return text.replace("\\", "\\\\")

347

.replace("\"", "\\\"")

348

.replace("\n", "\\n")

349

.replace("\r", "\\r")

350

.replace("\t", "\\t")

351

.replace("\b", "\\b")

352

.replace("\f", "\\f");

353

}

354

355

@Override

356

public String getName() {

357

return "JSON";

358

}

359

360

@Override

361

public String getMimeType() {

362

return "application/json";

363

}

364

365

@Override

366

public boolean isAutoEscapedByDefault() {

367

return true;

368

}

369

370

@Override

371

public boolean isOutputFormatMixingAllowed(OutputFormat otherOutputFormat) {

372

return otherOutputFormat instanceof JSONMarkupOutputFormat ||

373

otherOutputFormat instanceof PlainTextOutputFormat;

374

}

375

}

376

```

377

378

## Template Usage Examples

379

380

### Basic Auto-Escaping

381

382

```html

383

<!-- Template with HTML output format -->

384

<html>

385

<head>

386

<title>${pageTitle}</title>

387

</head>

388

<body>

389

<h1>${heading}</h1>

390

<p>${userComment}</p> <!-- Automatically escaped for HTML -->

391

<p>${safeHtmlContent?no_esc}</p> <!-- Bypass escaping when safe -->

392

</body>

393

</html>

394

```

395

396

### Format-Specific Escaping

397

398

```java

399

// Data model with potentially dangerous content

400

Map<String, Object> dataModel = new HashMap<>();

401

dataModel.put("userInput", "<script>alert('XSS')</script>");

402

dataModel.put("xmlContent", "<item>Value & \"quoted\"</item>");

403

404

// HTML template - userInput will be escaped

405

// Output: &lt;script&gt;alert(&#39;XSS&#39;)&lt;/script&gt;

406

407

// XML template - xmlContent will be escaped for XML

408

// Output: &lt;item&gt;Value &amp; &quot;quoted&quot;&lt;/item&gt;

409

410

// Plain text template - no escaping applied

411

// Output: <script>alert('XSS')</script>

412

```

413

414

### Mixed Output Formats

415

416

```html

417

<!-- Main HTML template -->

418

<html>

419

<body>

420

<script type="application/json">

421

<#-- Switch to JSON output format for this section -->

422

<#outputformat "JSON">

423

{

424

"userData": "${userData}", <!-- JSON-escaped -->

425

"message": "${message}" <!-- JSON-escaped -->

426

}

427

</#outputformat>

428

</script>

429

430

<div class="content">

431

${htmlContent} <!-- HTML-escaped -->

432

</div>

433

</body>

434

</html>

435

```

436

437

## Built-in Functions for Output Formats

438

439

### Format-Related Built-ins

440

441

```ftl

442

<!-- Check current output format -->

443

<#if .output_format == "HTML">

444

<!-- HTML-specific content -->

445

</#if>

446

447

<!-- Get output format name -->

448

Current format: ${.output_format}

449

450

<!-- Force escaping regardless of auto-escaping setting -->

451

${dangerousContent?esc}

452

453

<!-- Bypass escaping (use with caution) -->

454

${safeMarkup?no_esc}

455

456

<!-- Convert to different format -->

457

${htmlContent?markup_string} <!-- Get raw markup string -->

458

459

<!-- Check if content is already escaped -->

460

<#if content?is_markup_output>

461

Content is already escaped

462

</#if>

463

```

464

465

### Advanced Format Handling

466

467

```ftl

468

<!-- Conditional escaping based on format -->

469

<#if .output_format?starts_with("HTML")>

470

${content?html}

471

<#elseif .output_format == "XML">

472

${content?xml}

473

<#elseif .output_format == "RTF">

474

${content?rtf}

475

<#else>

476

${content}

477

</#if>

478

479

<!-- Format-aware concatenation -->

480

<#assign combined = markup1 + markup2> <!-- Properly combines markup -->

481

482

<!-- Convert between formats -->

483

<#outputformat "XML">

484

<#assign xmlEscaped = htmlContent?string>

485

</#outputformat>

486

```

487

488

## Security Considerations

489

490

### XSS Prevention

491

492

```java

493

// Proper configuration for web applications

494

Configuration cfg = new Configuration(Configuration.VERSION_2_3_34);

495

cfg.setOutputFormat(HTMLOutputFormat.INSTANCE);

496

cfg.setAutoEscapingPolicy(Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY);

497

498

// Template usage - automatic escaping prevents XSS

499

// ${userInput} -> automatically HTML-escaped

500

// ${trustedHtml?no_esc} -> bypasses escaping (use with extreme caution)

501

```

502

503

### Content Security Policies

504

505

```java

506

// Custom output format with CSP-aware escaping

507

public class CSPAwareHTMLOutputFormat extends HTMLOutputFormat {

508

@Override

509

public String escapePlainText(String plainTextContent) {

510

// Enhanced escaping for Content Security Policy compliance

511

return super.escapePlainText(plainTextContent)

512

.replace("javascript:", "blocked:")

513

.replace("data:", "blocked:")

514

.replace("vbscript:", "blocked:");

515

}

516

}

517

```

518

519

### Trusted Content Handling

520

521

```java

522

// Wrapper for trusted content that should not be escaped

523

public class TrustedContent implements TemplateScalarModel, TemplateMarkupOutputModel {

524

private final String content;

525

private final OutputFormat outputFormat;

526

527

public TrustedContent(String content, OutputFormat outputFormat) {

528

this.content = content;

529

this.outputFormat = outputFormat;

530

}

531

532

@Override

533

public String getAsString() throws TemplateModelException {

534

return content;

535

}

536

537

@Override

538

public OutputFormat getOutputFormat() {

539

return outputFormat;

540

}

541

}

542

543

// Usage

544

dataModel.put("trustedHtml", new TrustedContent("<b>Safe HTML</b>", HTMLOutputFormat.INSTANCE));

545

```

546

547

## Performance Considerations

548

549

### Output Format Selection Performance

550

551

```java

552

// Pre-configure output formats for better performance

553

Configuration cfg = new Configuration(Configuration.VERSION_2_3_34);

554

555

// Use template configurations for automatic format selection

556

cfg.setRecognizeStandardFileExtensions(true); // Automatic based on file extension

557

558

// Pre-register custom formats

559

Set<OutputFormat> customFormats = new HashSet<>();

560

customFormats.add(CustomJSONOutputFormat.INSTANCE);

561

customFormats.add(CustomXMLOutputFormat.INSTANCE);

562

cfg.setRegisteredCustomOutputFormats(customFormats);

563

```

564

565

### Escaping Performance

566

567

```java

568

// Efficient escaping for high-throughput applications

569

public class OptimizedHTMLOutputFormat extends HTMLOutputFormat {

570

private static final char[] HTML_ESCAPE_CHARS = {'&', '<', '>', '"', '\''};

571

572

@Override

573

public String escapePlainText(String plainTextContent) {

574

// Optimized escaping implementation

575

if (plainTextContent == null || plainTextContent.isEmpty()) {

576

return plainTextContent;

577

}

578

579

// Quick check if escaping is needed

580

boolean needsEscaping = false;

581

for (char c : HTML_ESCAPE_CHARS) {

582

if (plainTextContent.indexOf(c) != -1) {

583

needsEscaping = true;

584

break;

585

}

586

}

587

588

if (!needsEscaping) {

589

return plainTextContent;

590

}

591

592

// Perform escaping only when necessary

593

return super.escapePlainText(plainTextContent);

594

}

595

}

596

```