OpenPDF icon indicating copy to clipboard operation
OpenPDF copied to clipboard

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

Open balaji-pc opened this issue 3 months ago • 0 comments

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

balaji-pc avatar Sep 24 '25 18:09 balaji-pc