350 lines
9.6 KiB
Objective-C
350 lines
9.6 KiB
Objective-C
//
|
|
// WRCoreTextLayoutFrame.h
|
|
// WeRead
|
|
//
|
|
// Reverse-engineered from binary analysis
|
|
// Single page layout frame for WeRead's reading engine
|
|
//
|
|
|
|
#import <Foundation/Foundation.h>
|
|
#import <UIKit/UIKit.h>
|
|
#import <CoreText/CoreText.h>
|
|
|
|
@class WRBookCoverView;
|
|
@class WRDiscover;
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
#pragma mark - Layout Line
|
|
|
|
/**
|
|
* Represents a single line of laid-out text.
|
|
* Wrapper around CTLine with additional metadata.
|
|
*/
|
|
@interface WRCoreTextLayoutLine : NSObject
|
|
|
|
/** The CTLine this wraps */
|
|
@property (nonatomic, assign, readonly) CTLineRef ctLine;
|
|
|
|
/** Origin point of this line in the frame */
|
|
@property (nonatomic, assign) CGPoint origin;
|
|
|
|
/** The range of characters in this line */
|
|
@property (nonatomic, assign) NSRange stringRange;
|
|
|
|
/** Ascent of the line */
|
|
@property (nonatomic, assign, readonly) CGFloat ascent;
|
|
|
|
/** Desent of the line */
|
|
@property (nonatomic, assign, readonly) CGFloat descent;
|
|
|
|
/** Leading of the line */
|
|
@property (nonatomic, assign, readonly) CGFloat leading;
|
|
|
|
/** Total height of the line (ascent + descent + leading) */
|
|
@property (nonatomic, assign, readonly) CGFloat height;
|
|
|
|
/** Width of the line */
|
|
@property (nonatomic, assign, readonly) CGFloat width;
|
|
|
|
/** Whether this line is the last line in a paragraph */
|
|
@property (nonatomic, assign) BOOL isLastLineInParagraph;
|
|
|
|
@end
|
|
|
|
#pragma mark - WRCoreTextLayoutFrame
|
|
|
|
/**
|
|
* WRCoreTextLayoutFrame - Manages a single page of text layout.
|
|
*
|
|
* This class wraps CTFrame and provides high-level access to the
|
|
* layout of a single page. It handles:
|
|
* - Text rendering to CGContext
|
|
* - Image placement within text
|
|
* - avoidPageBreakInside CSS property
|
|
* - Render height calculation
|
|
* - Line-by-line access for hit testing and selection
|
|
*
|
|
* Architecture:
|
|
* - Contains CTFrame internally
|
|
* - Manages array of WRCoreTextLayoutLine objects
|
|
* - Supports direct drawing to CGContext
|
|
* - Handles image attachments inline
|
|
* - Integrates with RACSubject for reactive updates
|
|
*/
|
|
@interface WRCoreTextLayoutFrame : NSObject
|
|
|
|
#pragma mark - Core Layout Data
|
|
|
|
/** The attributed string that was laid out */
|
|
@property (nonatomic, strong, nullable) NSAttributedString *attributedString;
|
|
|
|
/** The range of the attributed string represented by this frame */
|
|
@property (nonatomic, assign) NSRange stringRange;
|
|
|
|
/** Array of WRCoreTextLayoutLine objects for this frame */
|
|
@property (nonatomic, strong, readonly) NSArray<WRCoreTextLayoutLine *> *layoutLines;
|
|
|
|
/** The bounding rectangle for this frame */
|
|
@property (nonatomic, assign) CGRect frame;
|
|
|
|
/** The CTFrame reference (accessor for internal use) */
|
|
@property (nonatomic, assign, readonly) CTFrameRef ctFrame;
|
|
|
|
#pragma mark - Layout Properties
|
|
|
|
/** Whether to avoid page breaks inside certain elements */
|
|
@property (nonatomic, assign) BOOL avoidPageBreakInside;
|
|
|
|
/** The rendered content height (may differ from frame height) */
|
|
@property (nonatomic, assign, readonly) CGFloat renderedContentHeight;
|
|
|
|
/** Padding around the content area */
|
|
@property (nonatomic, assign) UIEdgeInsets contentInsets;
|
|
|
|
/** Number of columns in this frame */
|
|
@property (nonatomic, assign) NSUInteger numberOfColumns;
|
|
|
|
/** Gap between columns */
|
|
@property (nonatomic, assign) CGFloat columnGap;
|
|
|
|
#pragma mark - Content Arrays
|
|
|
|
/** Array of attachment objects (images, etc.) */
|
|
@property (nonatomic, strong, nullable) NSArray *attachments;
|
|
|
|
/** Array of strikethrough ranges */
|
|
@property (nonatomic, strong, nullable) NSArray *strikethroughRanges;
|
|
|
|
/** Array of underline ranges */
|
|
@property (nonatomic, strong, nullable) NSArray *underlineRanges;
|
|
|
|
/** Array of highlight ranges */
|
|
@property (nonatomic, strong, nullable) NSArray *highlightRanges;
|
|
|
|
/** Set of selected line indices */
|
|
@property (nonatomic, strong, nullable) NSMutableSet *selectedLineIndices;
|
|
|
|
/** Current selection string */
|
|
@property (nonatomic, copy, nullable) NSString *selectionString;
|
|
|
|
/** Array of link ranges for tap handling */
|
|
@property (nonatomic, strong, nullable) NSArray *linkRanges;
|
|
|
|
/** Search result string */
|
|
@property (nonatomic, copy, nullable) NSString *searchResultString;
|
|
|
|
/** Array of search result ranges */
|
|
@property (nonatomic, strong, nullable) NSArray *searchResultRanges;
|
|
|
|
#pragma mark - Reactive Components
|
|
|
|
/** Subject for layout change notifications */
|
|
@property (nonatomic, strong, nullable) RACSubject *layoutChangeSubject;
|
|
|
|
#pragma mark - UI Elements
|
|
|
|
/** Long press gesture recognizer for text selection */
|
|
@property (nonatomic, strong, nullable) UILongPressGestureRecognizer *longPressRecognizer;
|
|
|
|
/** Book cover view (for cover pages) */
|
|
@property (nonatomic, strong, nullable) WRBookCoverView *bookCoverView;
|
|
|
|
/** Layer for custom drawing */
|
|
@property (nonatomic, strong, nullable) CALayer *customDrawingLayer;
|
|
|
|
/** Discover view (for social features) */
|
|
@property (nonatomic, strong, nullable) WRDiscover *discoverView;
|
|
|
|
/** Additional layout data */
|
|
@property (nonatomic, strong, nullable) NSArray *additionalLayoutData;
|
|
|
|
/** Frame identifier */
|
|
@property (nonatomic, copy, nullable) NSString *frameId;
|
|
|
|
/** Content identifier */
|
|
@property (nonatomic, copy, nullable) NSString *contentId;
|
|
|
|
/** Mutable dictionary for custom attributes */
|
|
@property (nonatomic, strong, nullable) NSMutableDictionary *customAttributes;
|
|
|
|
/** Mutable array for tracking visible elements */
|
|
@property (nonatomic, strong, nullable) NSMutableArray *visibleElements;
|
|
|
|
/** Mutable array for tracking accessibility elements */
|
|
@property (nonatomic, strong, nullable) NSMutableArray *accessibilityElements;
|
|
|
|
/** Mutable dictionary for caching computed values */
|
|
@property (nonatomic, strong, nullable) NSMutableDictionary *computedValueCache;
|
|
|
|
#pragma mark - Initialization
|
|
|
|
/**
|
|
* Initialize with a CTFrame and string range.
|
|
*
|
|
* @param ctFrame The CoreText frame
|
|
* @param range The string range
|
|
* @return Initialized layout frame
|
|
*/
|
|
- (instancetype)initWithCTFrame:(CTFrameRef)ctFrame
|
|
range:(NSRange)range;
|
|
|
|
/**
|
|
* Set the CTFrame and range (used by WRCoreTextLayouter).
|
|
*/
|
|
- (void)setCTFrame:(CTFrameRef)ctFrame range:(NSRange)range;
|
|
|
|
#pragma mark - Line Access
|
|
|
|
/**
|
|
* Returns the array of layout lines.
|
|
* This triggers line extraction from the CTFrame if not already done.
|
|
*
|
|
* @return Array of WRCoreTextLayoutLine objects
|
|
*/
|
|
- (NSArray<WRCoreTextLayoutLine *> *)lines;
|
|
|
|
/**
|
|
* Returns the number of lines in this frame.
|
|
*/
|
|
- (NSUInteger)lineCount;
|
|
|
|
/**
|
|
* Returns the layout line at the specified index.
|
|
*/
|
|
- (nullable WRCoreTextLayoutLine *)lineAtIndex:(NSUInteger)index;
|
|
|
|
#pragma mark - Height Calculation
|
|
|
|
/**
|
|
* Returns the rendered content height.
|
|
*
|
|
* This calculates the actual height of the rendered content,
|
|
* which may be less than the frame height if there's unused space.
|
|
*
|
|
* @return The height of the rendered content in points
|
|
*/
|
|
- (CGFloat)getRenderHeight;
|
|
|
|
/**
|
|
* Returns the height of the last line's descent.
|
|
* Used for precise baseline alignment.
|
|
*/
|
|
- (CGFloat)lastLineDescent;
|
|
|
|
#pragma mark - Drawing
|
|
|
|
/**
|
|
* Draw the layout frame content into a CGContext.
|
|
*
|
|
* This is the primary rendering method. It draws:
|
|
* 1. Text content using CTFrameDraw
|
|
* 2. Image attachments at their calculated positions
|
|
* 3. Decorative elements (strikethrough, underline, etc.)
|
|
*
|
|
* @param context The CGContext to draw into
|
|
* @param image Optional image to draw (for cover pages)
|
|
* @param size The size of the drawing area
|
|
* @param rect The rectangle to draw within
|
|
* @param position The position offset for the content
|
|
*/
|
|
- (void)drawInContext:(CGContextRef)context
|
|
image:(nullable UIImage *)image
|
|
size:(CGSize)size
|
|
inRect:(CGRect)rect
|
|
position:(CGPoint)position;
|
|
|
|
/**
|
|
* Draw only the text content (no images).
|
|
*/
|
|
- (void)drawTextInContext:(CGContextRef)context
|
|
inRect:(CGRect)rect;
|
|
|
|
/**
|
|
* Draw image attachments.
|
|
*/
|
|
- (void)drawAttachmentsInContext:(CGContextRef)context
|
|
inRect:(CGRect)rect;
|
|
|
|
#pragma mark - Page Break Avoidance
|
|
|
|
/**
|
|
* Removes the last lines if needed to avoid page breaks inside certain elements.
|
|
*
|
|
* This implements the CSS 'avoid-page-break-inside' property. When an element
|
|
* (like a table, code block, or list) cannot fit entirely on the current page,
|
|
* this method removes lines from the end until the element can fit.
|
|
*
|
|
* @return YES if lines were removed, NO otherwise
|
|
*/
|
|
- (BOOL)avoidPageBreakInsideByRemovingLastLinesIfNeeded;
|
|
|
|
/**
|
|
* Check if a range of text has the avoid-page-break-inside property.
|
|
*/
|
|
- (BOOL)shouldAvoidPageBreakInRange:(NSRange)range;
|
|
|
|
#pragma mark - Hit Testing
|
|
|
|
/**
|
|
* Returns the character index at a given point.
|
|
*
|
|
* @param point The point to test (in the frame's coordinate system)
|
|
* @return The character index, or NSNotFound if no character at that point
|
|
*/
|
|
- (NSUInteger)characterIndexAtPoint:(CGPoint)point;
|
|
|
|
/**
|
|
* Returns the line index at a given point.
|
|
*
|
|
* @param point The point to test
|
|
* @return The line index, or NSNotFound
|
|
*/
|
|
- (NSUInteger)lineIndexAtPoint:(CGPoint)point;
|
|
|
|
/**
|
|
* Returns the rect for a character at the given index.
|
|
*/
|
|
- (CGRect)rectForCharacterAtIndex:(NSUInteger)index;
|
|
|
|
#pragma mark - Selection
|
|
|
|
/**
|
|
* Returns the selected text string.
|
|
*/
|
|
- (nullable NSString *)selectedText;
|
|
|
|
/**
|
|
* Returns the ranges of selected text.
|
|
*/
|
|
- (NSArray<NSValue *> *)selectedRanges;
|
|
|
|
/**
|
|
* Selects text in the given range.
|
|
*/
|
|
- (void)selectTextInRange:(NSRange)range;
|
|
|
|
/**
|
|
* Clears the current selection.
|
|
*/
|
|
- (void)clearSelection;
|
|
|
|
#pragma mark - Search
|
|
|
|
/**
|
|
* Highlights search results within this frame.
|
|
*
|
|
* @param searchString The string to highlight
|
|
* @return The number of matches found
|
|
*/
|
|
- (NSUInteger)highlightSearchResults:(NSString *)searchString;
|
|
|
|
/**
|
|
* Clears all search result highlights.
|
|
*/
|
|
- (void)clearSearchHighlights;
|
|
|
|
@end
|
|
|
|
NS_ASSUME_NONNULL_END
|