Skip to content

About

A PHP port of the chenglou/pretext text layout and measurement library. Provides fast, accurate text layout calculation using ICU (Intl) for segmentation and an injectable font measurement interface.

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

Pretext PHP 8

A PHP 8 port of the pretext text layout and measurement library. Provides fast, accurate text layout calculation using ICU (Intl) for segmentation and an injectable font measurement interface.

Features

  • Fast text layout - O(n) complexity for measuring and laying out text
  • Internationalization - Full Unicode support via ICU (Intl extension)
  • Word segmentation - Proper handling of CJK, Thai, Arabic, Devanagari, and Myanmar
  • Bidirectional text - Bidi algorithm support for mixed LTR/RTL text
  • Injectable font measurement - Use stub measurer for testing or GD/FreeType for production
  • Comprehensive test suite - Full feature parity with the original TypeScript implementation

Important

This is a PHP port of the brilliant chenglou/pretext TypeScript library by Cheng Lou.

All credit for the algorithms, design, and implementation goes to Cheng Lou and the original authors. This port was created using Kimi K2.5 and maintains feature parity with the original, adapting it for PHP 8's type system and ecosystem.

Installation

composer require pretext/pretext

Requirements

  • PHP 8.0+
  • ext-intl (ICU for word/grapheme segmentation)
  • ext-mbstring (multibyte string functions)
  • ext-gd (optional, for production font measurement with FreeType)

Quick Start

use Pretext\PretextFactory;

// Prepare text for layout
$prepared = PretextFactory::prepare('Hello world', '16px Arial');

// Layout with specific max width and line height
$result = PretextFactory::layout($prepared, 200, 24);

echo "Lines: {$result->lineCount}\n";
echo "Height: {$result->height}px\n";

Usage

Basic Layout

use Pretext\Pretext;
use Pretext\Measurement\GdFontMeasurer;

// Production: Use GD/FreeType font measurement
$measurer = new GdFontMeasurer('/path/to/fonts', [
    'Arial' => 'arial.ttf',
    'Inter' => 'Inter-Regular.ttf',
]);
$pretext = new Pretext($measurer);

// Or use the static factory
$prepared = PretextFactory::prepare('Hello world', '16px Arial');
$result = PretextFactory::layout($prepared, 200, 24);

Rich Layout (with line details)

$prepared = $pretext->prepareWithSegments('Hello world this is a test', '16px Arial');
$lines = $pretext->layoutWithLines($prepared, 100, 24);

foreach ($lines->lines as $line) {
    echo "Line: {$line->text}\n";
    echo "Width: {$line->width}px\n";
    echo "Start: segment {$line->start->segmentIndex}\n";
}

Streaming Layout

$prepared = $pretext->prepareWithSegments('Long text here...', '16px Arial');
$cursor = new LayoutCursor(0, 0);

while (true) {
    $line = $pretext->layoutNextLine($prepared, $cursor, 200);
    if ($line === null) break;

    echo "Line: {$line->text}\n";
    $cursor = $line->end;
}

Custom Font Measurement

use Pretext\Measurement\FontMeasurerInterface;

class MyFontMeasurer implements FontMeasurerInterface
{
    public function measureText(string $text, string $font): float
    {
        // Your custom measurement logic
        return $width;
    }

    public function setFont(string $font): void
    {
        // Set current font
    }
}

$pretext = new Pretext(new MyFontMeasurer());

Internationalization

// Set locale for proper word segmentation
$pretext->setLocale('th');  // Thai
$thaiText = $pretext->prepare('ภาษาไทย', '16px Arial');

// Reset to default
$pretext->setLocale(null);

API Reference

Pretext Class

  • prepare(string $text, string $font): PreparedText - Prepare text for layout
  • prepareWithSegments(string $text, string $font): PreparedTextWithSegments - Prepare with segment details
  • layout(PreparedText $prepared, float $maxWidth, float $lineHeight): LayoutResult - Calculate line count and height
  • layoutWithLines(...): LayoutLinesResult - Layout with per-line details
  • walkLineRanges(...): int - Walk line ranges without materializing text
  • layoutNextLine(...): ?LayoutLine - Get next line from cursor position
  • clearCache(): void - Clear internal caches
  • setLocale(?string $locale): void - Set locale for word segmentation

PretextFactory Class

Static factory providing convenient access to all Pretext functionality.

Font Measurers

  • StubFontMeasurer - Testing/development (constant character widths)
  • GdFontMeasurer - Production (uses GD/FreeType with actual font files)

Architecture

The library follows a layered architecture:

Pretext (Public API)
  ↓
Analysis (Text segmentation & normalization)
  ↓
Bidi (Bidirectional text algorithm)
  ↓
Measurement (Text measurement via injectable interface)
  ↓
LineBreak (Line breaking algorithm)

Differences from TypeScript Original

  1. Font measurement - File-based (GD/FreeType) instead of browser Canvas
  2. Emoji correction - Always 0 (no DOM comparison available)
  3. EngineProfile - Always uses server/headless profile (no browser detection)
  4. Caching - Per-request by default (no shared memory)
  5. Bidi segment starts - Codepoint-indexed (cleaner, all bidi ranges are BMP)

Testing

# Install dependencies
composer install

# Run tests
./vendor/bin/phpunit

# Run with coverage
./vendor/bin/phpunit --coverage-html coverage

License

MIT

Credits

Based on the original TypeScript implementation by Sebastian Markbage and contributors.

About

A PHP port of the chenglou/pretext text layout and measurement library. Provides fast, accurate text layout calculation using ICU (Intl) for segmentation and an injectable font measurement interface.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages