This method helps to make the code more descriptive. Additionally, if you need to edit the constraints, you can do this only in the description of the dummy element. It lowers the chances of logical and syntactical errors when duplicating the code.
In the future versions of the product we plan to support creation of variables in the area of subelements of groups. The results of checking the constraints will then be stored in the values of different variables. The current method is a temporary solution (workaround) that helps to simplify the code in the Advanced sections in the present version of FlexiLayout Studio.
- The fields “Company name” and “Company address” are above the field “Invoice number”;
- The fields “Company name” and “Company address” are below the field “Invoice number”.
See Detecting dates in the case of low quality pre-recognition for more details on the methods of date search. Here we only describe the constraints for the date search in the current project.
{ RightOf: DateHeader.Rect.Right;
Below: DateHeader.Rect.Top - 30dt;
Above: DateHeader.Rect.Bottom + 30dt;
}
else
{ RectArray ar;
Let ar1 = Rect (InvoiceNum.Rect.Right, InvoiceNum.Rect.Top-30dt, PageRect.Right, InvoiceNum.Rect.Bottom + 30dt);
Let ar2 = Rect (InvoiceHeader.Rect.Left, InvoiceHeader.Rect.Bottom, InvoiceHeader.Rect.Right + 300dt, InvoiceHeader.Rect.Bottom + 150dt);
ar = RectArray (ar1);
ar.Add (ar2);
RestrictSearchArea (ar);
} If the name of the date field is detected (the constraint if not DateHeader.IsNull is checked), then the search will be carried out relative to the name of the date field: to the right of the name, on one horizontal level, with some margin of error for vertical displacement: { RightOf: DateHeader.Rect.Right;
Below: DateHeader.Rect.Top - 30dt;
Above: DateHeader.Rect.Bottom + 30dt;
} Otherwise the search area is divided into two rectangles: to the right of the invoice number and on the same level as the invoice number and below the invoice field.
For the sake of simplicity we assume that we have images of good quality where the field “Invoice number” and its name are always detected. In a real-life situation, before calling the properties of these elements we must run their IsNull-check, because if the elements are not detected, further search will be carried out relative to the search areas of the corresponding elements.

{if (not InvoiceHeader.IsNull) then
{ FuzzyQuality: Rect.Left - InvoiceHeader.Rect.Right, {0, 0, 0, 50000}*dt;
FuzzyQuality: Rect.Top - InvoiceHeader.Rect.Bottom, {-50000, 0, 0, 50000}*dt;
}
if (not InvoiceNum.IsNull) then
{ FuzzyQuality: Rect.Left - InvoiceNum.Rect.Right, {0, 0, 0, 50000}*dt;
FuzzyQuality: Rect.Top - InvoiceNum.Rect.Bottom, {-50000, 0, 0, 50000}*dt;
}
} It allows influencing the quality of the date hypothesis depending on its distance from the name of the invoice field and the field “Invoice number” itself if the name of the date field is not detected. The longer the distance to the specified fields, the greater the penalty will be for the corresponding hypotheses, i.e. we search for the date field nearest to the field “Invoice number”. Note. See Using Nearest and FuzzyQuality to look for elements for more details on the use of these functions. We specify identical search constraints for the element DateAsString, but in the Advanced post-search relations section we add one more line to the above-mentioned code: FuzzyQuality: 600dt - Width, {0, 0, 0, 50000}*dt; This line is needed during date search, so that a hypothesis with a longer string of alphabet characters is preferable to a shorter one. Additionally, on the Search Constraints tab of the element DateAsString we specified that, when searching for the date as a string of characters, the region of the element InvoiceNum must be excluded. This is because we decided, for the sake of simplicity, not to duplicate for the element DateAsString the same search constraints as we specified in the Advanced pre-search relations section of the element Date. We specified the search area as RestrictSearchArea (Date.Rect);. We thus told the program to search for the object of the element DateAsString in the area of the fuzzy rectangle of the element Date. The search area of the element Date can be represented as an array of rectangles. When a null hypothesis is generated for the element Date, the rectangle enclosing the search area is taken as a rectangle (Rect) of the current element. As you can see on the image below, it also encloses the field “Invoice number” described by the element InvoiceNum. Under some conditions (for instance, when the date field is very noisy), a situation may occur when, instead of the date field, the element DateAsString will detect the field of the invoice number, as characters (including digits) specified for this element do not have any format restrictions.

Let Date2 = InvoiceGroup.DateGroup.DateAsString;
Let DateGroup = InvoiceGroup.DateGroup;
Let InvoiceHeader = InvoiceGroup.InvoiceHeader;
Let InvoiceNumber = InvoiceGroup.InvoiceNum; if ((not InvoiceHeader.IsNull) or (not InvoiceNumber.IsNull)) and
(
((Date1.IsNull == FALSE) and ((max (InvoiceHeader.Rect.YCenter - Date1.Rect.YCenter, Date1.Rect.YCenter - InvoiceHeader.Rect.YCenter ) < 30dt) or (max (InvoiceNumber.Rect.YCenter - Date1.Rect.YCenter, Date1.Rect.YCenter - InvoiceNumber.Rect.YCenter ) < 30dt)) and ((Date1.Rect.Left - InvoiceHeader.Rect.Right > 100dt) or (Date1.Rect.Left - InvoiceNumber.Rect.Right > 50dt)))
or
((Date2.IsNull == FALSE) and ((max (InvoiceHeader.Rect.YCenter - Date2.Rect.YCenter, Date2.Rect.YCenter - InvoiceHeader.Rect.YCenter ) < 30dt) or (max (InvoiceNumber.Rect.YCenter - Date2.Rect.YCenter, Date2.Rect.YCenter - InvoiceNumber.Rect.YCenter ) < 30dt)) and ((Date2.Rect.Left - InvoiceHeader.Rect.Right > 100dt) or (Date2.Rect.Left - InvoiceNumber.Rect.Right > 50dt)))
)
then
{ Dontfind(); }
else
{ Below: PageRect.Top; } Despite the simplicity of constraints checked by this code, the code itself turned rather bulky. Its idea is that, if the date field detected by means of one of the elements Date or DateAsString is located to the right of the invoice number field but on the same level (with a vertical tolerance of 30 dt), then we tell the program that a null hypothesis must be generated for the auxiliary element. In the other cases the element ShamElement will be looked for below the top edge of the page. Since we created an auxiliary element of type Paragraph without any additional search constraints, it will enclose all text objects on the page and a single hypothesis will be generated. Nearly all the constraints to be checked are intuitively clear. So we are only going to describe the most complicated of them. max (InvoiceHeader.Rect.YCenter - Date1.Rect.YCenter, Date1.Rect.YCenter - InvoiceHeader.Rect.YCenter ) < 30dt This part of the code checks that the date field (in this case, if it is detected by the element Date) is located on one horizontal level with the element of the name of the field “Invoice number”. The date may be slightly higher or lower than the name (because of the possible errors during scanning or document filling). We specified that the maximum difference is 30 dt. We check the mutual vertical location of the field “Invoice date” and “Invoice number” by the same method. Date1.Rect.Left - InvoiceHeader.Rect.Right > 100dt This line checks that the date field is located to the right of the name of the field “Invoice number” (the coordinate of the left boundary of the date is greater than that of the right boundary of the name). We specified a tolerance of 100dt because between the name and the date there must be the invoice number itself. The location of the date field is to the right of the field “Invoice number”. All the conditions of the check are then duplicated for the element DateAsString. To check that our code is correct, let’s run the FlexiLayout matching procedure on all the pages. We see that on pages 1 and 3, where the date field is located to the right of the invoice number, a null hypothesis was generated for the element ShamElement. On the other two pages, text fragments were detected.

{ Below: InvoiceGroup.InvoiceHeader;
Below: InvoiceGroup.InvoiceNum;
NearestY: PageRect.Top;
}
else
{ Above: InvoiceGroup.InvoiceHeader;
Above: InvoiceGroup.InvoiceNum;
}
In this case we can use the function Nearest, as it helps to detect the field containing the company name unambiguously and accurately (there are no objects matching the same search constraints).
{ FuzzyQuality: Rect.Left - PageRect.Left, {0, 0, 0, 50000}*dt;
FuzzyQuality: Rect.Top - PageRect.Top, {0, 0, 0, 50000}*dt;
FuzzyQuality: 100dt - Height, {0, 0, 0, 10000}*dt;
} This code works when the single-line field “Company name” is looked for above the field of the invoice number. The sought fields don’t have names. At the same time, the mutual location of the fields “Company Name” and “Company address” is different on pages 2 and 4. The constraint Above: InvoiceGroup.InvoiceHeader; and Above: InvoiceGroup.InvoiceNum; is met not only be the string with the company name, but also by the strings in the address field.

{ Below: CompanyName;
Above: TotalSumHeader.Rect.Top;
}
else
{
Above: InvoiceGroup.InvoiceHeader;
Above: InvoiceGroup.InvoiceNum;
RightOf: CompanyName.Rect.Left;
Exclude: CompanyName;
} Here again we use the auxiliary element ShamElement, which helps to simplify the code. The creation of a FlexiLayout is now complete. Once we have run the FlexiLayout matching procedure we will see that all the fields have been successfully detected.
