code: def merge_nearby_textboxes_in_page(
      page,
      direction,
      max_nearby_dist,
      max_same_axis_dist,
      axis_align="center",
      merge_on_overlap=False,
      join_str=None,
  ):
      texts = page["texts"]
  
      if not texts:
          return
  
      if direction not in (DIRECTION_HORIZONTAL, DIRECTION_VERTICAL):
          raise ValueError(
              "direction must be `DIRECTION_HORIZONTAL` or `DIRECTION_VERTICAL` (see pdftabextract.common)"
          )
  
      if max_same_axis_dist < 0:
          raise ValueError("`max_same_axis_dist` must be positive")
  
      if direction == DIRECTION_HORIZONTAL:
          nearby_attr = "left"
          nearby_attr_other = "right"
          axis_attr = "top"
          axis_attr_dim = "height"
          if join_str is None:
              join_str = " "
      else:
          nearby_attr = "top"
          nearby_attr_other = "bottom"
          axis_attr = "left"
          axis_attr_dim = "width"
          if join_str is None:
              join_str = "\n"
  
      if not isinstance(join_str, str):
          raise ValueError("`join_str` must be a string")
  
      texts = sorted_by_attr(texts, axis_attr)
      if axis_align == "center":
          axes_gaps = np.array(
              [t[axis_attr] + t[axis_attr_dim] / 2 for t in texts]
          )
      else:
          axes_gaps = np.diff([t[axis_align] for t in texts])
      axes = find_clusters_1d_break_dist(
          axes_gaps, dist_thresh=max_same_axis_dist
      )
  
      merged_texts = []
      for t_indices in axes:
          t_positions = [
              (
                  texts[i],
                  texts[i][nearby_attr],
                  texts[i][nearby_attr_other],
              )
              for i in t_indices
          ]
          t_positions = sorted(t_positions, key=lambda x: x[1])
  
          merged_texts.append(t_positions[0][0])
  
          if len(t_positions) > 1:
              gaps = [
                  t_positions[i + 1][1] - p[2]
                  for i, p in enumerate(t_positions[:-1])
              ]
              for i, gap in enumerate(gaps):
                  t = t_positions[i + 1][0]
                  prev_t = merged_texts[-1]
  
                  if gap <= max_nearby_dist and (
                      merge_on_overlap or gap >= 0
                  ):  # merge
                      # merge texts
                      prev_t["value"] += join_str + t["value"]
                      prev_t["xmlnode"].text = prev_t["value"]
  
                      # update dimensions
                      if direction == DIRECTION_HORIZONTAL:
                          new_w = prev_t["width"] + (
                              t["right"] - prev_t["right"]
                          )
                          new_h = prev_t["height"]
                      else:
                          new_w = prev_t["width"]
                          new_h = prev_t["height"] + (
                              t["bottom"] - prev_t["bottom"]
                          )
  
                      update_text_dict_dim(
                          prev_t, (new_w, new_h), update_node=True
                      )
  
                      # remove this node from page
                      page["xmlnode"].remove(t["xmlnode"])
                  else:  # don't merge
                      merged_texts.append(t)
  
      assert len(merged_texts) <= len(texts)
  
      page["texts"] = merged_texts
  
file_path: pdftabextract/pdftabextract/clustering.py
imports: import itertools
  import numpy
  from scipy.stats import chisquare
  from pdftabextract import logger
  from pdftabextract.common import fill_array_a_with_values_from_b, sorted_by_attr, flatten_list, update_text_dict_dim, DIRECTION_HORIZONTAL, DIRECTION_VERTICAL
label_desc: Use image node_id 4 for calling a global function with example usage: merge_nearby_textboxes_in_page(page, direction, max_nearby_dist, max_same_axis_dist, axis_align, merge_on_overlap, join_str) without return types