Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Class to call inDesign from Python with test code #15

Open
scotth1963 opened this issue Jun 7, 2022 · 0 comments
Open

Class to call inDesign from Python with test code #15

scotth1963 opened this issue Jun 7, 2022 · 0 comments

Comments

@scotth1963
Copy link

===========================================================

indesign-class.py

Created: 07 JUNE 2022

author = 'Scott H'
version = '1.0'

"""
This is a class to generate an InDesign document
"""

"""
Instructions are below in the main
"""

===========================================================

import win32com.client
import os
from win32com.client import Dispatch
from enum import Enum
from PIL import Image
import pythoncom

class TextWrapOptions(Enum):
idBoundingBoxTextWrap = 1651729523 # from enum idTextWrapModes
idContour = 1835233134 # from enum idTextWrapModes
idJumpObjectTextWrap = 1650552420 # from enum idTextWrapModes
idNextColumnTextWrap = 1853384306 # from enum idTextWrapModes
idNone = 1852796517 # from enum idTextWrapModes

===========================================================

Values to use when definining text justification in a

textframe

===========================================================

class Justify(Enum):
AWAY_FROM_BINDING_SIDE = 1633772147 # from idJustification
CENTER_ALIGN = 1667591796 # from idJustification
CENTER_JUSTIFIED = 1667920756 # from idJustification
FULLY_JUSTIFIED = 1718971500 # from idJustification
LEFT_ALIGN = 1818584692 # from idJustification
LEFT_JUSTIFIED = 1818915700 # from idJustification
RIGHT_ALIGN = 1919379572 # from idJustification
RIGHT_JUSTIFIED = 1919578996 # from idJustification
TO_BINDING_SIDE = 1630691955 # from idJustification

===========================================================

Values to use when defining an image frame fit options

===========================================================

class FitOptions(Enum):
APPLY_FRAME_FITTING_OPTIONS = 1634100847 # from enum idFitOptions
CENTER_CONTENT = 1667591779 # from enum idFitOptions
CONTENT_AWARE_FIT = 1667327593 # from enum idFitOptions
CONTENT_TO_FRAME = 1668575078 # from enum idFitOptions
FILL_PROPORTIONALLY = 1718185072 # from enum idFitOptions
FRAME_TO_CONTENT = 1718906723 # from enum idFitOptions
PROPORTIONALLY = 1668247152 # from enum idFitOptions

===========================================================

Values to use when we vertically justify text

===========================================================

class TextVJust(Enum):
BOTTOM_ALIGN = 1651471469 # from enum idVerticalJustification
CENTER_ALIGN = 1667591796 # from enum idVerticalJustification
JUSTIFY_ALIGN = 1785951334 # from enum idVerticalJustification
TOP_ALIGN = 1953460256 # from enum idVerticalJustification

========================================================================

========================================================================

====== InDesign Process ======

====== ======

====== This class will generate an inDesign Document. ======

====== Steps to creating an inDesign Document: ======

====== ======

====== 1 - Create an app cursor by attaching to C library ======

====== 2 - Use app cursor to create a in memory document ======

====== 3 - Set the Page (starting with page 1) ======

====== ======

========================================================================

========================================================================

class inDesign_Process:
def init(self):
self.in_des_app = self.set_in_des_app()
self.in_des_doc = self.set_in_des_doc()
self.page_ptr = 1 # defines the page we are on
self.lst_flds = []
self.lst_ty = []
self.lst_tx = []
self.lst_by = []
self.lst_bx = []

    self.text_start = 4  # where to start text
    self.text_end = 49  # when to start a new line
    self.in_des_page = self.set_page()
    self.geo_bound_ptr = self.reset_geo_bnds()
    self.file_name = ""
    self.dct_letter_weight = {
        "&": 0.75,
        "-": 0.6,
        "1": 0.6,
        "2": 0.6,
        "3": 0.6,
        "4": 0.65,
        "5": 0.65,
        "6": 0.7,
        "7": 0.7,
        "8": 0.65,
        "9": 0.65,
        "0": 0.65,
        ",": 0.3,
        ".": 0.3,
        ":": 0.3,
        " ": 0.2,
        "a": 0.6,
        "b": 0.6,
        "c": 0.57,
        "d": 0.6,
        "e": 0.65,
        "f": 0.36,
        "g": 0.65,
        "h": 0.65,
        "i": 0.3,
        "j": 0.3,
        "k": 0.6,
        "l": 0.3,
        "m": 0.9,
        "n": 0.65,
        "o": 0.65,
        "p": 0.65,
        "q": 0.65,
        "r": 0.375,
        "s": 0.6,
        "t": 0.375,
        "u": 0.6,
        "v": 0.6,
        "w": 0.85,
        "x": 0.6,
        "y": 0.6,
        "z": 0.6,
        "A": 0.75,
        "B": 0.75,
        "C": 0.75,
        "D": 0.75,
        "E": 0.75,
        "F": 0.6,
        "G": 0.75,
        "H": 0.75,
        "I": 0.375,
        "J": 0.6,
        "K": 0.75,
        "L": 0.6,
        "M": 1,
        "N": 0.75,
        "O": 0.75,
        "P": 0.75,
        "Q": 0.75,
        "R": 0.75,
        "S": 0.75,
        "T": 0.75,
        "U": 0.75,
        "V": 0.75,
        "W": 1,
        "X": 0.75,
        "Y": 0.75,
        "Z": 0.75,
    }

# ===========================================================
# Setup Step 1 Create an App Cursor
# ===========================================================

def set_in_des_app(self):
    return win32com.client.Dispatch(
        "InDesign.Application.2020", pythoncom.CoInitialize()
    )

# ===========================================================
# Setup Step 2 Create Document from App Cursor
# ===========================================================

def set_in_des_doc(self):
    return self.in_des_app.Documents.Add()

# ===========================================================
# Setup Step 3 Create Page in Document
# ===========================================================
def set_page(self):
    self.geo_bound_ptr = self.reset_geo_bnds() # Fixing Image position maybe
    
    if self.page_ptr != 1:
        self.in_des_doc.Pages.Add()
    return self.in_des_doc.Pages.Item(self.page_ptr)

# ===========================================================
# Get a Text frame to be used above
# ===========================================================
def get_text_frame(self):
    return self.in_des_page.TextFrames.Add()

# ===========================================================
# Get a new image rectangle when adding image (see add_image)
# ===========================================================
def get_image_rectangle(self):
    return self.in_des_page.Rectangles.Add()

# ===========================================================
# Move to the next page by
#    - incrementing the page pointer
#    - set the next inDesign page to write to
#    - reset the Geo Boundary pointer to be at top of page
# ===========================================================
def set_next_page(self, imgs_on_page=2):
    
    self.page_ptr += 1
    self.in_des_page = self.set_page()
    self.geo_bound_ptr = self.reset_geo_bnds()

# ===========================================================
# Set the name of the file to create (full path)
# ===========================================================
def set_filename(self, file_in):
    self.file_name = file_in

# ===========================================================
# Save the file from memory onto disk at the file_name
# defined above
# ===========================================================

def save_in_des_file(self):
    ret_value = False
    directory = os.path.dirname(self.file_name)
    try:
        # If file path does not exist, create directory
        if not os.path.exists(directory):
            os.makedirs(directory)
        # If file path exist, save the file to the path
        if os.path.exists(directory):
            self.in_des_doc.Save(self.file_name)
        ret_value = True
    except Exception as e:
        print("Export to inDesign failed: " + str(e))
    return ret_value

# ===========================================================
# Close the inDesign Document when done
# ===========================================================
def close_des_file(self):
    self.in_des_doc.Close()

# ===========================================================
# ====                                                  =====
# ====                  IMAGE FUNCTIONS                 =====
# ====                                                  =====
# ===========================================================
    

# ===========================================================
# get the image size to be used to calculate geo boundaries
# when adding an image. image size is a tuple of
# (width, height) (see add_image)
# ===========================================================
def get_image_size(self, img_in):
    im = Image.open(img_in)
    ret_value = im.size
    im.close()
    return ret_value

# ===========================================================
# get_image_thumb_size will get the size of the image 
# thumbnail. If the image is taller than wider we use
# 400 pixels, if wider than taller we use 900 pixels. This 
# will return the size tuple (width, height) of a thumbnail
# that keeps image perspective.
# ===========================================================
def get_image_thumb_size(self, img_in):
    img = Image.open(img_in)
    img_size = img.size
    img_width = img_size[0]
    img_height = img_size[1]
    if img_width > img_height :
        img.thumbnail((900, 900), Image.ANTIALIAS)
    else:
        img.thumbnail((300, 300), Image.ANTIALIAS)
    ret_value = img.size
    img.close()
    return ret_value
    
def get_img_per(self,img_size_in,thumb_size_in):
    lst_ret_value = []
    for item in range(len(thumb_size_in)):
        lst_ret_value.append(int(thumb_size_in[item]/img_size_in[item]*100))        
    return tuple(lst_ret_value)
    

# ===========================================================
# ====                                                  =====
# ====             GROUPING FUNCTIONALITY               =====
# ====                                                  =====
# ===========================================================




# ===========================================================
# start_grouping - resets the list of fields and geoboundaries
# so a new set of textboxes can be grouped together
# ===========================================================
def start_grouping(self):
    self.lst_flds = []
    self.lst_ty = []
    self.lst_tx = []
    self.lst_by = []
    self.lst_bx = []

# ===========================================================
# add_to_group will take the field passed in, save that in 
# the list of fields, get the geoboundary of the textbox and
# save each item in its appropriate list (top y in ty, etc)
# ===========================================================
def add_to_group(self,fld_in):
    self.lst_flds.append(fld_in)
    geo_bnd = fld_in.GeometricBounds
    self.lst_ty.append(geo_bnd[0])
    self.lst_tx.append(geo_bnd[1])
    self.lst_by.append(geo_bnd[2])
    self.lst_bx.append(geo_bnd[3])
    
# ===========================================================
# end_grouping will take all the textframes stored and group
# them together.
# ===========================================================
def end_grouping(self):
    lst_items = self.lst_flds[:]
    group_ret_value = lst_items[0]
    for item in range(1,len(lst_items)):
        lst_items[item].PreviousTextFrame = lst_items[item-1]
    for item in range(1,len(lst_items)):
        lst_items[item].Delete()

    self.lst_ty.sort()   
    self.lst_tx.sort()   
    self.lst_by.sort(reverse=True)   
    self.lst_bx.sort(reverse=True)   
    
    top_y = int(self.lst_ty[0])
    top_x = int(self.lst_tx[0])
    bot_y = int(self.lst_by[0])
    bot_x = int(self.lst_bx[0])
    
    lst_group_bound = ["0","0","0","0"]
    lst_group_bound = self.set_top_y(lst_group_bound,top_y)
    lst_group_bound = self.set_top_x(lst_group_bound,top_x)
    lst_group_bound = self.set_bot_y(lst_group_bound,bot_y)
    lst_group_bound = self.set_bot_x(lst_group_bound,bot_x)
    group_ret_value.NextTextFrame = None
    group_ret_value.Fit(FitOptions.FRAME_TO_CONTENT.value) 
    group_ret_value.GeometricBounds = lst_group_bound
    group_ret_value.TextFramePreferences.VerticalJustification = (
            TextVJust.CENTER_ALIGN.value
        )
    self.geo_bound_ptr = lst_group_bound
    # print(f"after group :{self.geo_bound_ptr}")
    self.geo_bnds_next_line()  # start on new line
    # print(f"after group next line:{self.geo_bound_ptr}")


# ===========================================================
# ====                                                  =====
# ====             GEOBOUND FUNCTIONALITY               =====
# ====                                                  =====
# ===========================================================
# ===========================================================
# Geometric Bounds Functions
#
# Geometric Bounds [ty, tx, by, bx] or (y1, x1, y2, x2)
# maps the top left the bottom right of a box
# NOTE: y is first, by using a set of wrapper functions
# that will be something that the user won't need to remember
# ===========================================================

# ===========================================================
# Reset Geo Boundary - when starting a new page, the
# geo boundary should be reset to the top of the page
# ===========================================================
def reset_geo_bnds(self):
    ret_value = ["1", "2", "3", "4"]  # geo bound placeholders
    ret_value = self.set_top_x(ret_value, self.text_start)
    ret_value = self.set_top_y(ret_value, self.text_start)
    ret_value = self.set_bot_x(ret_value, self.text_start)
    ret_value = self.set_bot_y(ret_value, self.text_start + 1)
    return ret_value


# ===========================================================
# setting ty of [ty, tx, by, bx]
# val_in is an integer (ex 4)
# we make 4p0 as ty
# ===========================================================
def set_top_y(self, geo_bnd_in, val_in):
    ret_value = geo_bnd_in[:]
    top_x_adj = f"{val_in}p0"
    ret_value[0] = top_x_adj
    return ret_value

# ===========================================================
# setting tx of [ty, tx, by, bx]
# val_in is an integer (ex 4)
# we make 4p0 as tx
# ===========================================================
def set_top_x(self, geo_bnd_in, val_in):
    ret_value = geo_bnd_in[:]
    top_y_adj = f"{val_in}p0"
    ret_value[1] = top_y_adj
    return ret_value

# ===========================================================
# setting by of [ty, tx, by, bx]
# val_in is an integer (ex 4)
# we make 4p0 as by
# ===========================================================
def set_bot_y(self, geo_bnd_in, val_in):
    ret_value = geo_bnd_in[:]
    bot_x_adj = f"{val_in}p0"
    ret_value[2] = bot_x_adj
    return ret_value

# ===========================================================
# setting bx of [ty, tx, by, bx]
# val_in is an integer (ex 4)
# we make 4p0 as bx
# ===========================================================
def set_bot_x(self, geo_bnd_in, val_in):
    ret_value = geo_bnd_in[:]
    bot_y_adj = f"{val_in}p0"
    ret_value[3] = bot_y_adj
    return ret_value

# ===========================================================
# getting ty of [ty, tx, by, bx]
# removing the p0 from ty (ex 4p0)
# returning the integer 4
# ===========================================================
def get_top_y(self, geo_bnd_in):
    return int(geo_bnd_in[0].split("p")[0])

# ===========================================================
# getting tx of [ty, tx, by, bx]
# removing the p0 from tx (ex 4p0)
# returning the integer 4
# ===========================================================
def get_top_x(self, geo_bnd_in):
    return int(geo_bnd_in[1].split("p")[0])

# ===========================================================
# getting by of [ty, tx, by, bx]
# removing the p0 from by (ex 4p0)
# returning the integer 4
# ===========================================================
def get_bot_y(self, geo_bnd_in):
    return int(geo_bnd_in[2].split("p")[0])

# ===========================================================
# getting bx of [ty, tx, by, bx]
# removing the p0 from bx (ex 4p0)
# returning the integer 4
# ===========================================================
def get_bot_x(self, geo_bnd_in):
    return int(geo_bnd_in[3].split("p")[0])


# ===========================================================
# Geometric Boundaries Next Line
#
# This function will go through setting the geo boundaries
# to go to the next line.
#   - Move the new top y to bot y
#   - Add 1 to bot y
#   - Set botx and topx to text_start (defined in init)
#   - Return the new Geo Boundary
# ===========================================================

def geo_bnds_next_line(self):
    next_geo = self.geo_bound_ptr[:]
    # set top equal to bottom
    bot_y_val = self.get_bot_y(next_geo)
    next_geo = self.set_top_y(next_geo, bot_y_val)
    bot_y_val += 1  # add 1
    next_geo = self.set_bot_y(next_geo, bot_y_val)
    # set to self.text_start+2 if you want to indent
    next_geo = self.set_top_x(next_geo, self.text_start)
    next_geo = self.set_bot_x(next_geo, self.text_start)
    self.geo_bound_ptr = next_geo



        
# ===========================================================
# Add Image PUBLIC FACING FUNCTION
#
# This function will go through the process of adding an
# image to the page
#   - Check to see if this is a new page if not move down
#     4 lines
#   - Get a new image rectangle
#   - Get the size of the image (length,height)
#   - Get the image rectange geo boundary size (rec_geo_bnd)
#   - Set the image size to 30%
#   - readjust the geo boundary for text under image
# ===========================================================

def add_image(self, img_in):
    my_frame = self.get_image_rectangle()
    img_size = self.get_image_size(img_in)
    thumb_size = self.get_image_thumb_size(img_in)
    #print(f"thumb_size :{thumb_size}")
    rec_geo_bnd = self.geo_get_image_spacing(thumb_size)
    rec_geo_bnd = self.image_geo_adjust(rec_geo_bnd,thumb_size)
    #print(f"rec_geo_bnd :{rec_geo_bnd}")
    rec_geo_bnd = self.geo_bnd_page_adj(rec_geo_bnd) # adjust for page
    my_frame.GeometricBounds = rec_geo_bnd
    my_graphic_list = my_frame.Place(img_in)
    horiz_per,vert_per = self.get_img_per(img_size,thumb_size)
    #print(f"horiz_per:{horiz_per}  vert_per: {vert_per}")
    my_frame.HorizontalScale = horiz_per
    my_frame.VerticalScale = vert_per
    
    my_object_style = self.in_des_doc.ObjectStyles.Add()
    my_object_style.EnableStroke = True
    my_object_style.StrokeWeight = 3
    my_object_style.StrokeType = self.in_des_doc.StrokeStyles.Item("Solid")
    my_object_style.StrokeColor = self.in_des_doc.Colors.Item("Black")
    my_frame.ApplyObjectStyle(my_object_style, True)
    my_frame.Fit(FitOptions.FRAME_TO_CONTENT.value)
    # ===================================================
    # if we use the geo bounds for images, they move
    # the text too far down, we need to adjust the
    # geo boundaries to more "text like" before
    # updating geo_bound_ptr
    # ===================================================
    self.geo_bound_ptr = self.text_geo_adjust(rec_geo_bnd, thumb_size)
    self.geo_bnds_next_line()  # start on newline for text

    

def px_to_geo(self,val_in):
    return int(val_in/35)

def image_geo_adjust(self, geo_bnd_in, thumb_size_in):
    ret_value = geo_bnd_in[:]
    bot_y = self.get_bot_y(ret_value)
    delta_y = self.px_to_geo(thumb_size_in[1])
    bot_y = bot_y + delta_y
    ret_value = self.set_bot_y(ret_value,bot_y)
    return ret_value

def geo_get_image_spacing(self, thumb_size_in):
    ret_value = self.geo_bound_ptr[:]  # start where text left off
    #print(f"1 image_spacing :{ret_value}")
    top_x = self.get_top_x(ret_value)
    bot_x = self.get_bot_x(ret_value)
    top_y = self.get_top_y(ret_value)
    bot_y = self.get_bot_y(ret_value)
    if top_x < 6:
        top_x = 6
    if top_y < 6:
        top_y = 6
    else:
        thumb_height = thumb_size_in[1]
        delta_y = self.px_to_geo(thumb_height)
        top_y = top_y + delta_y
        bot_y = bot_y + delta_y
    thumb_width = thumb_size_in[0]
    geo_wdth = self.px_to_geo(thumb_width)
    delta_x = int((49 - geo_wdth)/2)
    top_x = top_x + delta_x
    bot_x = bot_x + delta_x
    ret_value = self.set_top_x(ret_value,top_x)
    ret_value = self.set_bot_x(ret_value,bot_x)
    ret_value = self.set_top_y(ret_value,top_y)
    ret_value = self.set_bot_y(ret_value,bot_y)
    #print(f"2 image_spacing :{ret_value}")
    return ret_value


def text_geo_adjust(self, geo_bnd_in, thumb_size_in):
    ret_value = geo_bnd_in[:]
    top_y = self.get_top_y(ret_value)
    delta_y = self.px_to_geo(thumb_size_in[1])
    top_y = top_y + delta_y
    ret_value = self.set_top_y(ret_value,top_y)
    return ret_value



# ===========================================================
# Geo Boundary Page Adjustment.
#
# Odd # pages except 1 (ex 3,5,7) need to have the top and
# bottom x moved over to appear on the right page. 55 seems
# to be a good number.
# ===========================================================
def geo_bnd_pg_adj(self, geo_bnd_in):
    ret_value = geo_bnd_in[:]
    if self.page_ptr % 2 == 1 and self.page_ptr > 1:
        top_x = self.get_top_x(ret_value)
        bot_x = self.get_bot_x(ret_value)
        ret_value = self.set_top_x(ret_value, top_x + 45)
        ret_value = self.set_bot_x(ret_value, bot_x + 45)
    return ret_value

# ===========================================================
# Text Functions Public Facing Functions
# ===========================================================

# ===========================================================
# Wrapper to get the next line
# ===========================================================
def set_next_line(self):
    self.geo_bnds_next_line()

# ===========================================================
# Add text defined as a label
# - Text is bolded
# - Text is right aligned
# ===========================================================

def add_label(self, word_in):
    self.geo_bnds_right(word_in)
    label_frame = self.get_text_frame()
    geo_bound_adj = self.geo_bnd_page_adj(self.geo_bound_ptr)
    label_frame.TextFramePreferences.VerticalJustification = (
        TextVJust.CENTER_ALIGN.value
    )
    label_frame.GeometricBounds = geo_bound_adj
    label_frame.texts[0].pointSize = "12pt"
    label_frame.ParentStory.AppliedFont = "Arial"
    label_frame.ParentStory.FontStyle = "Bold"
    label_frame.ParentStory.Justification = Justify.RIGHT_ALIGN.value  # Right Align
    label_frame.Contents = word_in
    self.add_to_group(label_frame)

# ===========================================================
# Add text defined as plain text
# - if the text is going over the end of the line go to the
#   next line
# ===========================================================
def add_text(self, word_in, split_text=False):
    word_weight = int(self.get_word_points(word_in))
    # print(f"word_in :{word_in}")
    self.geo_bnds_right(word_in)
    text_frame = self.get_text_frame()
    geo_bound_adj = self.geo_bound_ptr # don't adjust till afterwards
    if word_weight+self.text_start > self.text_end:
        bot_y = self.get_bot_y(geo_bound_adj)
        geo_bound_adj = self.set_top_x(geo_bound_adj, self.text_start)
        geo_bound_adj = self.set_bot_x(geo_bound_adj, self.text_end - 1)
        if (word_weight / self.text_end).is_integer():
            bot_y_delta = int(word_weight / self.text_end)
        else:
            bot_y_delta = int(word_weight / self.text_end) + 2
        geo_bound_adj = self.set_bot_y(geo_bound_adj, bot_y + bot_y_delta + 1)
        self.geo_bound_ptr = self.set_bot_y(
            self.geo_bound_ptr, bot_y + bot_y_delta + 1
        )  # set geo pointer's bottom y
    geo_bound_adj = self.geo_bnd_page_adj(geo_bound_adj) # see if moving after sizing will put on proper page

    # print(f"geo_bound_adj after :{geo_bound_adj}")
    text_frame.TextFramePreferences.VerticalJustification = (
        TextVJust.CENTER_ALIGN.value
    )
    text_frame.GeometricBounds = geo_bound_adj
    text_frame.texts[0].pointSize = "12pt"
    text_frame.ParentStory.AppliedFont = "Arial"
    text_frame.Contents = word_in + " "
    self.add_to_group(text_frame)

# ===========================================================
# Add text defined as a title
# - text is bolded
# ===========================================================
def add_title(self, word_in):
    word_weight = int(self.get_word_points(word_in))
    self.geo_bnds_right(word_in)
    title_frame = self.get_text_frame()
    geo_bound_adj = self.geo_bnd_page_adj(self.geo_bound_ptr)
    geo_bound_adj = self.geo_bound_ptr # don't adjust till afterwards
    if word_weight+self.text_start > self.text_end:
        bot_y = self.get_bot_y(geo_bound_adj)
        geo_bound_adj = self.set_top_x(geo_bound_adj, self.text_start)
        geo_bound_adj = self.set_bot_x(geo_bound_adj, self.text_end - 1)
        if (word_weight / self.text_end).is_integer():
            bot_y_delta = int(word_weight / self.text_end)
        else:
            bot_y_delta = int(word_weight / self.text_end) + 2
        geo_bound_adj = self.set_bot_y(geo_bound_adj, bot_y + bot_y_delta + 1)
        self.geo_bound_ptr = self.set_bot_y(
            self.geo_bound_ptr, bot_y + bot_y_delta + 1
        )  # set geo pointer's bottom y
    geo_bound_adj = self.geo_bnd_page_adj(geo_bound_adj) # see if moving after sizing will put on proper page
    title_frame.TextFramePreferences.VerticalJustification = (
        TextVJust.CENTER_ALIGN.value
    )
    title_frame.GeometricBounds = geo_bound_adj
    title_frame.texts[0].pointSize = "12pt"
    title_frame.ParentStory.AppliedFont = "Arial"
    title_frame.ParentStory.FontStyle = "Bold"
    title_frame.Contents = word_in + " "
    self.add_to_group(title_frame)

# ===========================================================
# Geo Boundary right
#
# Find out the spacing for the words in the textbox and make
# the textbox the proper size. Set the Geo boundary
# accordingly.
# ===========================================================
def geo_bnds_right(self, word_in):
    new_line_pos = self.geo_get_word_spacing(word_in)
    if new_line_pos >= self.text_end:
        self.geo_bnds_next_line()
        new_line_pos = self.geo_get_word_spacing(word_in)

# ===========================================================
# Get Geo Boundary word spacing
#
# This function will take the current geo boundary and
# add the amount of space needed for the text to store in
# this textbox
# ===========================================================
def geo_get_word_spacing(self, word_in):
    ret_value = self.geo_bound_ptr[:]
    top_x_val = self.get_top_x(ret_value)
    bot_x_val = self.get_bot_x(ret_value)
    ret_value = self.set_top_x(ret_value, bot_x_val)
    right_append = self.get_word_points(word_in)  # the space the word needs
    new_line_pos = int(right_append) + bot_x_val + 1
    ret_value = self.set_bot_x(ret_value, new_line_pos)
    self.geo_bound_ptr = ret_value
    return new_line_pos

# ===========================================================
# get word points
#
# Will get the weighted values of letters defined in
# dct_letter_weight. after a cummulative number is reached
# that's passed back and the integer value is used.
# ===========================================================
def get_word_points(self, word_in):
    ret_value = 0.0
    for letter in word_in:
        if letter in self.dct_letter_weight.keys():
            ret_value += self.dct_letter_weight[letter]
    return ret_value

# ===========================================================
# Geo Boundary Page Adjustment.
#
# Odd # pages except 1 (ex 3,5,7) need to have the top and
# bottom x moved over to appear on the right page. 51 seems
# to be a good number for text
# ===========================================================

def geo_bnd_page_adj(self, geo_bnd_in):
    ret_value = geo_bnd_in[:]
    if self.page_ptr % 2 == 1 and self.page_ptr > 1:
        top_x = self.get_top_x(ret_value)
        bot_x = self.get_bot_x(ret_value)
        ret_value = self.set_top_x(ret_value, top_x + 51)
        ret_value = self.set_bot_x(ret_value, bot_x + 51)
    return ret_value

if name == 'main':
# ========================================================================
# TEST INDESIGN CLASS
#
# THIS PROGRAM IS FREE SOFTWARE. IT COMES WITHOUT ANY WARRANTY, TO
# THE EXTENT PERMITTED BY APPLICABLE LAW. YOU CAN REDISTRIBUTE IT
# AND/OR MODIFY IT UNDER SIMILAR TERMS. THIS CODE IS DISTRIBUTED "AS IS".
# THE USER ACCEPTS ALL RESPONSIBILITIES
#
# This class will put an image above, add text below, and group all the
# text into one textframe at the end. Most of my calculations were eye-
# balling the output. If anyone has a more accurate conversion (like
# pixels to geo boundary units), please feel free to modify and repost.
#
# I use the general space of the characters in Arial font at 12 point
# to calculate the weight (width) of a set of text. Again, the weight
# of each character was what I saw.
#
# WHAT YOU NEED TO DO TO RUN THIS EXAMPLE
#
# You need to add any images below. I use a string var
# to define wide and long images.
# You also need to put in the full path of the output file you want
# to generate.
# The images I put I use a % by calculating the thumbnail and using
# that for calculations.
#
# THINGS I FOUND
#
# One weird thing I found is that odd pages over 1, the position
# is actually a full page over. geo_bnd_page_adj takes care of that
#
# The GeoBoundary Group of coordinates are in the order y,x and not
# x,y the way I've always known coordinates. That's why I added
# gets and sets for top_x,top_y,bot_x and bot_y, to avoid confusing
# myself.
#
# InDesign comes up with a new COM object to address which seems to
# corrispond with the year of the InDesign release. I have InDesign
# 2020 so my call in set_in_des_app is to "InDesign.Application.2020".
# Change the year suffix to match the version of InDesign you're working
# with. In the past it seems the same COM, just rebundled. Hopefully,
# they'll keep it similar in future releases.
#
# ========================================================================
lst_words = ["this is an","unscientific","way:","to","see","how:","words","go","together:", "adding more", "stuff", "on", "the", "end"]
img_long = 'C:\full\path\to\long\image" #<====put your images here
img_wide = 'C:\full\path\to\wide\image" #<====put your images here
img_small = 'C:\full\path\to\small\image" #<====put your images here

# 2 items on a page
# long, wide
#lst_imgs = [img_long,img_wide]

# wide, long
#lst_imgs = [img_wide, img_long]

# long, long
#lst_imgs = [img_long, img_long]

# wide, wide
#lst_imgs = [img_wide,img_wide]

#small, big
#lst_imgs = [img_small,img_wide]


# 3 items on a page 
# long, wide, long
#lst_imgs = [img_long,img_wide,img_long]

# wide, long, wide
#lst_imgs = [img_wide, img_long, img_wide]

# long, long, long
#lst_imgs = [img_long, img_long, img_long]

# wide, wide, wide
#lst_imgs = [img_wide,img_wide, img_wide]



in_des_doc = inDesign_Process()
for img_item in lst_imgs:
    in_des_doc.add_image(img_item)
    in_des_doc.start_grouping() # start grouping text frames together under image
    for word in lst_words:
        if "this is" in word:
            id_test = in_des_doc.add_title(word)
        elif ":" in word:
            in_des_doc.add_label(word)
        else:
            in_des_doc.add_text(word)
    in_des_doc.end_grouping() # merge text frames
#in_des_doc.set_next_page() # Will go to the next page 

in_des_doc.set_filename(r'C:\full\path\to\output\filename.indd')  #<=====put your full path and filename to generate here
if in_des_doc.save_in_des_file():
    print("SUCCESS!!!!")
else:
    print("FAIL!!!!!!")
in_des_doc.close_des_file()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant