[Ticket cloned from T21977: PDF Export - format question title]
Hi there
The spacing between questions is quite large, are we able to reduce space?
Thanks in advance.
[Ticket cloned from T21977: PDF Export - format question title]
Hi there
The spacing between questions is quite large, are we able to reduce space?
Thanks in advance.
Hello Miriam,
Thank you for the update.
To remove the empty space, implement a custom brick for your question type and override the setYBottom
function to set the _yBot
property value to 0.
Consider the following demo: View Demo.
JavaScriptimport { FlatRepository, FlatQuestion, PdfBrick } from "survey-pdf";
const CUSTOM_TYPE = "clause";
class CustomPdfBrick extends PdfBrick {
setYBottom(val) {
this._yBot = 0;
}
}
class FlatCustomQuestion extends FlatQuestion {
async generateFlatsContent(point) {
const rect = { ...point };
rect.yBot = point.yTop + 20;
rect.xRight = point.xLeft + 100;
return [new CustomPdfBrick(this.question, this.controller, rect)];
}
}
I hope this option helps. Please let me know if you have any further questions.
JavaScriptimport React from "react";
import { ElementFactory, Question, Serializer, SvgRegistry } from "survey-core";
import { localization, PropertyGridEditorCollection } from "survey-creator-core";
import { EmptyBrick, FlatQuestion, FlatRepository, IPoint, IRect, PdfBrick, TextBrick } from "survey-pdf";
import {
ReactQuestionFactory,
SurveyQuestionElementBase
} from "survey-react-ui";
const CUSTOM_TYPE = "clause";
class FlatClause extends FlatQuestion {
async generateFlatsContent(point: IRect): Promise<PdfBrick[]> {
const rect = { ...point };
rect.yBot = point.yTop;
rect.xRight = point.xLeft + 100;
return [];
}
}
FlatRepository.register(CUSTOM_TYPE, FlatClause);
// Create a question model
export class QuestionClauseModel extends Question {
getType() {
return CUSTOM_TYPE;
}
}
// Register the custom "clause" question type
export function registerClause() {
ReactQuestionFactory.Instance.registerQuestion(CUSTOM_TYPE, (props) => {
return React.createElement(SurveyQuestionClause, props);
});
ElementFactory.Instance.registerElement(
CUSTOM_TYPE,
(name) => {
return new QuestionClauseModel(name);
},
true
);
// Register `clause` as an editor for properties in the Survey Creator's Property Grid
PropertyGridEditorCollection.register({
fit: function (prop) {
return prop.type === CUSTOM_TYPE;
},
getJSON: function () {
return { type: CUSTOM_TYPE };
}
});
}
const locale = localization.getLocale("");
locale.qt[CUSTOM_TYPE] = "Clause";
// Register an SVG icon for the question type
SvgRegistry.registerIconFromSvg(
CUSTOM_TYPE,
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#7c7c7c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-signature"><path d="m21 17-2.156-1.868A.5.5 0 0 0 18 15.5v.5a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1c0-2.545-3.991-3.97-8.5-4a1 1 0 0 0 0 5c4.153 0 4.745-11.295 5.708-13.5a2.5 2.5 0 1 1 3.31 3.284"/><path d="M3 21h18"/></svg>'
);
// Add question type metadata for further serialization into JSON
Serializer.addClass(
CUSTOM_TYPE,
[],
function () {
return new QuestionClauseModel("");
},
"question"
);
// Create a class that renders the clause
export class SurveyQuestionClause extends SurveyQuestionElementBase {
constructor(props: any) {
super(props);
}
get question() {
return this.questionBase;
}
renderElement() {
return null;
}
}
// Remember to call registerClause() somewhere in your application startup
JSON{
"title": "Individual employment agreement - Spotlight",
"pages": [
{
"name": "page1",
"elements": [
{
"type": "clause",
"name": "question1",
"title": "<p>This is a clause This is a clause This is a clause This is a clause This is a clause This is a clause This is a clause This is a clause This is a clause This is a clause This is a clause This is a clause This is a clause </p>"
},
{
"type": "clause",
"name": "question2",
"title": "<p>This is a clause</p>"
},
{
"type": "clause",
"name": "question4",
"title": "<p>Question Title</p>"
},
{
"type": "clause",
"name": "question3",
"title": "<p>Question Title</p>"
}
]
}
],
"code": "DEA"
}
as you can see here the lower 3 have perfect bottom spacing but if you increase the character size of the title shown in question 1 the gap returns these are not titlebricks anymore the are html bricks due to the title being encased in <p> tags
Hi Miriam,
Thank you for sharing this code. I may need additional time to create a demo and test it on my end. Please stay tuned.
Hello Miriam,
Please accept my apologies for the delayed reply. To reduce the gap which remains when an input field is not rendered, please follow the previous recommendation and set the _yBot
property to 0
at the level of a custom brick:
JavaScriptclass CustomPdfBrick extends PdfBrick {
setYBottom(val) {
this._yBot = 0;
}
}
class FlatClause extends FlatQuestion {
async generateFlatsContent(point: IRect): Promise<PdfBrick[]> {
const rect = { ...point };
rect.yBot = point.yTop;
rect.xRight = point.xLeft + 100;
return [new CustomPdfBrick(this.question, this.controller, rect)];
}
}
Let me know if this option works for you. Additionally, please clarify whether you intentionally render unformatted HTML tags within question titles? Take note that you can convert Markdown to HTML by registering a Markdown text processor for your Form Library and PDF: View Demo.
Let me know if you require further assistance processing the Markdown text.
Thanks
Hi Miriam,
Judging by your screenshots, your questions do not have any input area for some reason. Would you share an example of a target survey JSON model for research?
To answer your question the developer i have helping me with this has created a custom component called "Clause" this component does not render any user input elements it is technically just a question title with no user input fields. Attached is the basic code that initializes the custom component.
import React from "react"; import { ElementFactory, Question, Serializer, SvgRegistry } from "survey-core"; import { localization, PropertyGridEditorCollection } from "survey-creator-core"; import { ReactQuestionFactory, SurveyQuestionElementBase } from "survey-react-ui"; const CUSTOM_TYPE = "clause"; // Create a question model export class QuestionClauseModel extends Question { getType() { return CUSTOM_TYPE; } } // Register the custom "clause" question type export function registerClause() { ReactQuestionFactory.Instance.registerQuestion(CUSTOM_TYPE, (props) => { return React.createElement(SurveyQuestionClause, props); }); ElementFactory.Instance.registerElement( CUSTOM_TYPE, (name) => { return new QuestionClauseModel(name); }, true ); // Register `clause` as an editor for properties in the Survey Creator's Property Grid PropertyGridEditorCollection.register({ fit: function (prop) { return prop.type === CUSTOM_TYPE; }, getJSON: function () { return { type: CUSTOM_TYPE }; } }); } const locale = localization.getLocale(""); locale.qt[CUSTOM_TYPE] = "Clause"; // Register an SVG icon for the question type SvgRegistry.registerIconFromSvg( CUSTOM_TYPE, '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#7c7c7c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-signature"><path d="m21 17-2.156-1.868A.5.5 0 0 0 18 15.5v.5a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1c0-2.545-3.991-3.97-8.5-4a1 1 0 0 0 0 5c4.153 0 4.745-11.295 5.708-13.5a2.5 2.5 0 1 1 3.31 3.284"/><path d="M3 21h18"/></svg>' ); // Add question type metadata for further serialization into JSON Serializer.addClass( CUSTOM_TYPE, [], function () { return new QuestionClauseModel(""); }, "question" ); // Create a class that renders the clause export class SurveyQuestionClause extends SurveyQuestionElementBase { constructor(props: any) { super(props); } get question() { return this.questionBase; } renderElement() { return null; } }
The spacing issue i am refering too is when i go to generate a pdf with multiple of these clauses it seems to have a gap between them holding space for a user input field attached will be an image of the survey creator layout and what the pdf generates as well as the json markup of the questions.
{ "title": "test", "pages": [ { "name": "page1", "elements": [ { "type": "clause", "name": "question1", "title": "This is a test clause to show that the spacing between clauses seems to be very out of wack and am not sure why, Number 1" }, { "type": "clause", "name": "question2", "title": "This is a test clause to show that the spacing between clauses seems to be very out of wack and am not sure why, Number 2" }, { "type": "clause", "name": "question3", "title": "This is a test clause to show that the spacing between clauses seems to be very out of wack and am not sure why, Number 3" }, { "type": "text", "name": "question4", "title": "Basic input question 1" }, { "type": "text", "name": "question5", "title": "Basic input question 2" } ] } ], "code": "EEAD" }
There seems to be an empty text brick inside the brick composition according to my developer is there a way to remove bricks based on a predicate?
Eg: if a bricks text property is empty remove it from the options
its always the last brick in the title bricks
opt.bricks[0].bricks