Skip to content

API to set custom first-line baseline Y in ColumnText (e.g., setFirstLineBaselineY / setFirstLineOffsetFromTop) #1430

@balaji-pc

Description

@balaji-pc

When placing text with ColumnText in a rectangular area, it is hard to precisely control the baseline Y of the first rendered line inside the column. Today, the first line’s baseline is derived from yLine and the effective leading (yLine − leading). In practice, this becomes non‑intuitive and error‑prone when:
setSimpleColumn resets yLine to the top (ury), forcing extra calculations.

  • Effective leading can be a mix of fixed and multiplied leading.
  • Flags like setUseAscender and setAdjustFirstLine can affect first-line placement.
  • Switching between addText(...) and addElement(Paragraph/…) changes how first-line spacing behaves.

The result is that aligning the first line to an exact baseline Y (for forms, labels, receipts, or precise grid layouts) requires trial‑and‑error and knowledge of several interacting flags, rather than a simple, explicit API.

Note:
ColumnText does expose setYLine(float) and documents that “The line will be written to yLine − leading”
ColumnText.java . However, this still requires callers to precompute the correct yLine from a desired baseline and the effective leading, and to account for ascender/first‑line adjustments and composite vs. simple text flows.

Suggested Solution:
Introduce an explicit and ergonomic way to set the first line’s baseline Y inside the column, independent from internal leading/ascender logic. For example:

  1. Compute the firstLine without currentLeading
  2. A direct setter:
    - setFirstLineBaselineY(float baselineY)
    - Guarantees the first line’s baseline equals baselineY, regardless of setUseAscender, setAdjustFirstLine,
    fixed/multiplied leading, or whether the content was added via addText or addElement.
  3. A relative alternative:
    - setFirstLineOffsetFromTop(float offset)
    - Starts the first line’s baseline at (ury − offset) inside the current simple column.
  4. an overload to setSimpleColumn:
    - setSimpleColumn(llx, lly, urx, ury, float firstLineBaselineY)
    - setSimpleColumn(llx, lly, urx, ury, float offsetFromTop, boolean offsetIsFromTop)

Additional notes:

 - This API should only control the first line; subsequent lines continue with normal leading and pagination.
 - Document clear precedence rules vs. setYLine, setUseAscender, setAdjustFirstLine, and leading.
 - Provide examples for both addText(...) and addElement(...) usage.

Current feature example

     `PdfContentByte cb = writer.getDirectContent();

      ColumnText ct = new ColumnText(cb);

      float llx = 50f, lly = 500f, urx = 300f, ury = 700f;

      ct.setSimpleColumn(llx, lly, urx, ury);

      ct.setUseAscender(false);
      
      float dAscent =font.getBaseFont.getAscentPoint("d", font.getSize);
      float baseline = ury-dAscent
      
       // We want firstYLine == baseline but ColumnText writes firstYLine = yLine - leading
      ct.setYLine(baseline); //Note: must be yLine  < ury && yLine > lly
      Chunk chunk = new Chunk("Hello, world\nWelcome to OpenPdf", font);
      chunk.setCharacterSpacing(0.4f);
      chunk.setHorizontalScaling(1f);

      Paragraph paragraph = new Paragraph(chunk)
      paragraph.setLeading(14f)
      ct.addText(paragraph);
      ct.go();`

To be short:

Actual:
yLine = ury - dAscent
firstYLine = (ury - dAscent) - currentLeading = 694.68 - 14 = 680.68

Expected:
dAscent = 5.32f
baseline(yLine) = ury - dAscent
firstYline = yLine = 694.68

Your real name
Balaji

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions